@niivue/niivue 0.26.3 → 0.28.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.
package/dist/niivue.es.js CHANGED
@@ -364,6 +364,26 @@ function translate(out, a, v) {
364
364
  }
365
365
  return out;
366
366
  }
367
+ function scale$1(out, a, v) {
368
+ var x2 = v[0], y = v[1], z = v[2];
369
+ out[0] = a[0] * x2;
370
+ out[1] = a[1] * x2;
371
+ out[2] = a[2] * x2;
372
+ out[3] = a[3] * x2;
373
+ out[4] = a[4] * y;
374
+ out[5] = a[5] * y;
375
+ out[6] = a[6] * y;
376
+ out[7] = a[7] * y;
377
+ out[8] = a[8] * z;
378
+ out[9] = a[9] * z;
379
+ out[10] = a[10] * z;
380
+ out[11] = a[11] * z;
381
+ out[12] = a[12];
382
+ out[13] = a[13];
383
+ out[14] = a[14];
384
+ out[15] = a[15];
385
+ return out;
386
+ }
367
387
  function rotateX(out, a, rad) {
368
388
  var s = Math.sin(rad);
369
389
  var c2 = Math.cos(rad);
@@ -747,6 +767,31 @@ vec4 applyClip (vec3 dir, inout vec4 samplePos, inout float len, inout bool isCl
747
767
  samplePos = vec4(samplePos.xyz+dir * disBackFace, disBackFace);
748
768
  }
749
769
  return samplePos;
770
+ }
771
+ float frac2ndc(vec3 frac) {
772
+ //https://stackoverflow.com/questions/7777913/how-to-render-depth-linearly-in-modern-opengl-with-gl-fragcoord-z-in-fragment-sh
773
+ vec4 pos = vec4(frac.xyz, 1.0); //fraction
774
+ vec4 dim = vec4(vec3(textureSize(volume, 0)), 1.0);
775
+ pos = pos * dim;
776
+ vec4 shim = vec4(-0.5, -0.5, -0.5, 0.0);
777
+ pos += shim;
778
+ vec4 mm = transpose(matRAS) * pos;
779
+ float z_ndc = (mvpMtx * vec4(mm.xyz, 1.0)).z;
780
+ return (z_ndc + 1.0) / 2.0;
781
+ }
782
+ vec4 drawColor(float scalar) {
783
+ vec4 dcolor = vec4(0.0, 0.0, 0.0, 0.0);
784
+ if (scalar <= 0.0) return dcolor;
785
+ dcolor.a = drawOpacity;
786
+ if (scalar >= (4.0/255.0))
787
+ dcolor.rgb = vec3(scalar,0.0,scalar);
788
+ else if (scalar >= (3.0/255.0))
789
+ dcolor.b = 1.0;
790
+ else if (scalar >= (2.0/255.0))
791
+ dcolor.g = 1.0;
792
+ else
793
+ dcolor.r = 1.0;
794
+ return dcolor;
750
795
  }`;
751
796
  var fragRenderShader = `#version 300 es
752
797
  #line 14
@@ -763,26 +808,15 @@ uniform float backOpacity;
763
808
  uniform mat4 mvpMtx;
764
809
  uniform mat4 matRAS;
765
810
  uniform vec4 clipPlaneColor;
811
+ uniform float drawOpacity;
812
+ uniform highp sampler3D drawing;
766
813
  in vec3 vColor;
767
814
  out vec4 fColor;
768
- ` + kRenderFunc + `
769
- float frac2ndc(vec3 frac) {
770
- //https://stackoverflow.com/questions/7777913/how-to-render-depth-linearly-in-modern-opengl-with-gl-fragcoord-z-in-fragment-sh
771
- vec4 pos = vec4(frac.xyz, 1.0); //fraction
772
- vec4 dim = vec4(vec3(textureSize(volume, 0)), 1.0);
773
- pos = pos * dim;
774
- vec4 shim = vec4(-0.5, -0.5, -0.5, 0.0);
775
- pos += shim;
776
- vec4 mm = transpose(matRAS) * pos;
777
- float z_ndc = (mvpMtx * vec4(mm.xyz, 1.0)).z;
778
- return (z_ndc + 1.0) / 2.0;
779
-
780
- }
781
- void main() {
782
- if (fColor.x > 2.0) {
783
- fColor = vec4(1.0, 0.0, 0.0, 0.5);
784
- return;
785
- }
815
+ ` + kRenderFunc + `void main() {
816
+ if (fColor.x > 2.0) {
817
+ fColor = vec4(1.0, 0.0, 0.0, 0.5);
818
+ return;
819
+ }
786
820
  fColor = vec4(0.0,0.0,0.0,0.0);
787
821
  vec4 clipPlaneColorX = clipPlaneColor;
788
822
  //if (clipPlaneColor.a < 0.0)
@@ -818,23 +852,20 @@ void main() {
818
852
  vec4 deltaDirFast = vec4(dir.xyz * stepSizeFast, stepSizeFast);
819
853
  while (samplePos.a <= len) {
820
854
  float val = texture(volume, samplePos.xyz).a;
821
- if (val > 0.01) {
822
- break;
823
- }
855
+ if (val > 0.01)
856
+ break;
824
857
  samplePos += deltaDirFast; //advance ray position
825
858
  }
826
- if ((samplePos.a >= len) && ((overlays < 1.0) || (backgroundMasksOverlays > 0))) {
827
- if (isClip){
859
+ if ((samplePos.a >= len) && (((overlays < 1.0) && (drawOpacity <= 0.0) ) || (backgroundMasksOverlays > 0))) {
860
+ if (isClip)
828
861
  fColor += clipPlaneColorX;
829
- }
830
862
  return;
831
863
  }
832
864
  fColor = vec4(1.0, 1.0, 1.0, 1.0);
833
865
  //gl_FragDepth = frac2ndc(samplePos.xyz); //crude due to fast pass resolution
834
866
  samplePos -= deltaDirFast;
835
- if (samplePos.a < 0.0){
867
+ if (samplePos.a < 0.0)
836
868
  vec4 samplePos = vec4(start.xyz, 0.0); //ray position
837
- }
838
869
  //end: fast pass
839
870
  vec4 colAcc = vec4(0.0,0.0,0.0,0.0);
840
871
  vec4 firstHit = vec4(0.0,0.0,0.0,2.0 * lenNoClip);
@@ -856,51 +887,52 @@ void main() {
856
887
  break;
857
888
  }
858
889
  }
859
- if (firstHit.a < len){
890
+ if (firstHit.a < len)
860
891
  gl_FragDepth = frac2ndc(firstHit.xyz);
861
- }
862
892
  colAcc.a = (colAcc.a / earlyTermination) * backOpacity;
863
893
  fColor = colAcc;
864
894
  //if (isClip) //CR
865
895
  if ((isColorPlaneInVolume) && (clipPos.a != samplePos.a) && (abs(firstHit.a - clipPos.a) < deltaDir.a))
866
896
  fColor.rgb = mix(fColor.rgb, clipPlaneColorX.rgb, abs(clipPlaneColor.a));
867
897
  //fColor.rgb = mix(fColor.rgb, clipPlaneColorX.rgb, clipPlaneColorX.a * 0.65);
868
- if (overlays < 1.0) {
869
- return;
870
- }
898
+ if ((overlays < 1.0) && (drawOpacity <= 0.0))
899
+ return;
871
900
  //overlay pass
872
901
  len = lenNoClip;
873
902
  samplePos = vec4(start.xyz, 0.0); //ray position
874
903
  //start: OPTIONAL fast pass: rapid traversal until first hit
875
- stepSizeFast = sliceSize * 1.9;
904
+ stepSizeFast = sliceSize * 1.0;
876
905
  deltaDirFast = vec4(dir.xyz * stepSizeFast, stepSizeFast);
877
906
  while (samplePos.a <= len) {
878
907
  float val = texture(overlay, samplePos.xyz).a;
879
- if (val > 0.01) {
880
- break;
881
- }
908
+ if (drawOpacity > 0.0)
909
+ val = max(val, texture(drawing, samplePos.xyz).r);
910
+ if (val > 0.001)
911
+ break;
882
912
  samplePos += deltaDirFast; //advance ray position
883
913
  }
884
914
  if (samplePos.a >= len) {
885
- if (isClip && (fColor.a == 0.0)) {
886
- fColor += clipPlaneColorX;
887
- }
888
- return;
915
+ if (isClip && (fColor.a == 0.0))
916
+ fColor += clipPlaneColorX;
917
+ return;
889
918
  }
890
919
  samplePos -= deltaDirFast;
891
- if (samplePos.a < 0.0){
920
+ if (samplePos.a < 0.0)
892
921
  vec4 samplePos = vec4(start.xyz, 0.0); //ray position
893
- }
894
922
  //end: fast pass
895
923
  float overFarthest = len;
896
924
  colAcc = vec4(0.0, 0.0, 0.0, 0.0);
925
+
897
926
  samplePos += deltaDir * ran; //jitter ray
898
927
  vec4 overFirstHit = vec4(0.0,0.0,0.0,2.0 * len);
899
- if (backgroundMasksOverlays > 0){
928
+ if (backgroundMasksOverlays > 0)
900
929
  samplePos = firstHit;
901
- }
902
930
  while (samplePos.a <= len) {
903
931
  vec4 colorSample = texture(overlay, samplePos.xyz);
932
+ if ((colorSample.a < 0.01) && (drawOpacity > 0.0)) {
933
+ float val = texture(drawing, samplePos.xyz).r;
934
+ colorSample = drawColor(val);
935
+ }
904
936
  samplePos += deltaDir; //advance ray position
905
937
  if (colorSample.a >= 0.01) {
906
938
  if (overFirstHit.a > len)
@@ -913,19 +945,19 @@ void main() {
913
945
  break;
914
946
  }
915
947
  }
916
- if (samplePos.a >= len) {
917
- if (isClip && (fColor.a == 0.0)){
948
+ //if (samplePos.a >= len) {
949
+ if (colAcc.a <= 0.0) {
950
+ if (isClip && (fColor.a == 0.0))
918
951
  fColor += clipPlaneColorX;
919
- }
920
952
  return;
921
953
  }
954
+
922
955
  //if (overFirstHit.a < len)
923
956
  gl_FragDepth = frac2ndc(overFirstHit.xyz);
924
957
  float overMix = colAcc.a;
925
958
  float overlayDepth = 0.3;
926
- if (fColor.a <= 0.0){
927
- overMix = 1.0;
928
- }
959
+ if (fColor.a <= 0.0)
960
+ overMix = 1.0;
929
961
  else if (((overFarthest) > backNearest)) {
930
962
  float dx = (overFarthest - backNearest)/1.73;
931
963
  dx = fColor.a * pow(dx, overlayDepth);
@@ -1016,10 +1048,10 @@ void main(void) {
1016
1048
  texPos = vec3(pos.x, pos.y, slice);
1017
1049
  if (axCorSag > 1)
1018
1050
  texPos = vec3(slice, pos.x, pos.y);
1019
- else if (axCorSag > 0)
1051
+ else if (axCorSag > 0)
1020
1052
  texPos = vec3(pos.x, slice, pos.y);
1021
1053
  vec4 mm = frac2mm * vec4(texPos, 1.0);
1022
- //vec4 mm = vec4(texPos, 1.0) * frac2mm;
1054
+ //vec4 mm = vec4(texPos, 1.0) * frac2mm;
1023
1055
  gl_Position = mvpMtx * mm;
1024
1056
  //gl_Position = mm;
1025
1057
  }`;
@@ -1102,45 +1134,6 @@ void main() {
1102
1134
  float fmap = (0.5 + layer) / nlayer;
1103
1135
  color = vec4(texture(colormap, vec2(vColor.x, fmap)).rgb, 1.0);
1104
1136
  }`;
1105
- var vertGraphShader = `#version 300 es
1106
- #line 229
1107
- layout(location=0) in vec3 pos;
1108
- uniform float thickness;
1109
- uniform vec2 canvasWidthHeight;
1110
- uniform vec4 leftTopRightBottom;
1111
- void main(void) {
1112
- //convert pixel x,y space 1..canvasWidth,1..canvasHeight to WebGL 1..-1,-1..1
1113
- float ny = (leftTopRightBottom.x-leftTopRightBottom.z);
1114
- float nx = (leftTopRightBottom.y-leftTopRightBottom.w);
1115
- float len = sqrt((nx*nx)+(ny*ny));
1116
- if (len > 0.0) {
1117
- nx = 0.5*thickness*nx/len;
1118
- ny = 0.5*thickness*ny/len;
1119
- }
1120
- vec2 frac;
1121
- if (pos.y < 0.5)
1122
- frac.y = leftTopRightBottom.y;
1123
- else
1124
- frac.y = leftTopRightBottom.w;
1125
- if (pos.x < 0.5) {
1126
- frac.x = leftTopRightBottom.x;
1127
- frac.y = leftTopRightBottom.y;
1128
- } else {
1129
- frac.x = leftTopRightBottom.z;
1130
- frac.y = leftTopRightBottom.w;
1131
- }
1132
- if (pos.y < 0.5) {
1133
- frac.x += nx;
1134
- frac.y -= ny;
1135
- } else {
1136
- frac.x -= nx;
1137
- frac.y += ny;
1138
- }
1139
- frac.x /= canvasWidthHeight.x;
1140
- frac.y = 1.0 - (frac.y / canvasWidthHeight.y);
1141
- frac = (frac * 2.0) - 1.0;
1142
- gl_Position = vec4(frac, 0.0, 1.0);
1143
- }`;
1144
1137
  var vertRectShader = `#version 300 es
1145
1138
  #line 229
1146
1139
  layout(location=0) in vec3 pos;
@@ -1219,13 +1212,13 @@ uniform float screenPxRange;
1219
1212
  in vec2 vUV;
1220
1213
  out vec4 color;
1221
1214
  float median(float r, float g, float b) {
1222
- return max(min(r, g), min(max(r, g), b));
1215
+ return max(min(r, g), min(max(r, g), b));
1223
1216
  }
1224
1217
  void main() {
1225
1218
  vec3 msd = texture(fontTexture, vUV).rgb;
1226
1219
  float sd = median(msd.r, msd.g, msd.b);
1227
- float screenPxDistance = screenPxRange*(sd - 0.5);
1228
- float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
1220
+ float screenPxDistance = screenPxRange*(sd - 0.5);
1221
+ float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
1229
1222
  color = vec4(fontColor.rgb , fontColor.a * opacity);
1230
1223
  }`;
1231
1224
  var vertOrientShader = `#version 300 es
@@ -1235,8 +1228,8 @@ precision highp float;
1235
1228
  in vec3 vPos;
1236
1229
  out vec2 TexCoord;
1237
1230
  void main() {
1238
- TexCoord = vPos.xy;
1239
- gl_Position = vec4( (vPos.xy-vec2(0.5,0.5)) * 2.0, 0.0, 1.0);
1231
+ TexCoord = vPos.xy;
1232
+ gl_Position = vec4( (vPos.xy-vec2(0.5,0.5)) * 2.0, 0.0, 1.0);
1240
1233
  }`;
1241
1234
  var fragOrientShaderU = `#version 300 es
1242
1235
  uniform highp usampler3D intensityVol;
@@ -1261,42 +1254,45 @@ uniform float opacity;
1261
1254
  uniform vec3 xyzFrac;
1262
1255
  uniform mat4 mtx;
1263
1256
  void main(void) {
1264
- vec4 vx = vec4(TexCoord.x, TexCoord.y, coordZ, 1.0) * mtx;
1265
- uint idx = texture(intensityVol, vx.xyz).r;
1266
- FragColor = vec4(0.0, 0.0, 0.0, 0.0);
1267
- if (idx == uint(0))
1268
- return;
1269
- if (xyzFrac.x > 0.0) { //outline
1270
- vx = vec4(TexCoord.x+xyzFrac.x, TexCoord.y, coordZ, 1.0) * mtx;
1271
- uint R = texture(intensityVol, vx.xyz).r;
1272
- vx = vec4(TexCoord.x-xyzFrac.x, TexCoord.y, coordZ, 1.0) * mtx;
1273
- uint L = texture(intensityVol, vx.xyz).r;
1274
- vx = vec4(TexCoord.x, TexCoord.y+xyzFrac.y, coordZ, 1.0) * mtx;
1275
- uint A = texture(intensityVol, vx.xyz).r;
1276
- vx = vec4(TexCoord.x, TexCoord.y-xyzFrac.y, coordZ, 1.0) * mtx;
1277
- uint P = texture(intensityVol, vx.xyz).r;
1278
- vx = vec4(TexCoord.x, TexCoord.y, coordZ+xyzFrac.z, 1.0) * mtx;
1279
- uint S = texture(intensityVol, vx.xyz).r;
1280
- vx = vec4(TexCoord.x, TexCoord.y, coordZ-xyzFrac.z, 1.0) * mtx;
1281
- uint I = texture(intensityVol, vx.xyz).r;
1282
- if ((idx == R) && (idx == L) && (idx == A) && (idx == P) && (idx == S) && (idx == I))
1283
- return;
1284
- }
1285
- idx = ((idx - uint(1)) % uint(100))+uint(1);
1286
- float fx = (float(idx)+0.5) / 256.0;
1287
- float nlayer = float(textureSize(colormap, 0).y) * 0.5; //0.5 as both each layer has positive and negative color slot
1288
- float y = (2.0 * layer + 1.0)/(4.0 * nlayer);
1289
- //float y = (2.0 * layer + 1.0)/(4.0 * numLayers);
1290
- FragColor = texture(colormap, vec2(fx, y)).rgba;
1291
- FragColor.a *= opacity;
1292
- if (layer < 2.0) return;
1293
- vec2 texXY = TexCoord.xy*0.5 +vec2(0.5,0.5);
1294
- vec4 prevColor = texture(blend3D, vec3(texXY, coordZ));
1295
- // https://en.wikipedia.org/wiki/Alpha_compositing
1296
- float aout = FragColor.a + (1.0 - FragColor.a) * prevColor.a;
1297
- if (aout <= 0.0) return;
1298
- FragColor.rgb = ((FragColor.rgb * FragColor.a) + (prevColor.rgb * prevColor.a * (1.0 - FragColor.a))) / aout;
1299
- FragColor.a = aout;
1257
+ vec4 vx = vec4(TexCoord.x, TexCoord.y, coordZ, 1.0) * mtx;
1258
+ uint idx = texture(intensityVol, vx.xyz).r;
1259
+ FragColor = vec4(0.0, 0.0, 0.0, 0.0);
1260
+ if (idx == uint(0))
1261
+ return;
1262
+ if (xyzFrac.x > 0.0) { //outline
1263
+ vx = vec4(TexCoord.x+xyzFrac.x, TexCoord.y, coordZ, 1.0) * mtx;
1264
+ uint R = texture(intensityVol, vx.xyz).r;
1265
+ vx = vec4(TexCoord.x-xyzFrac.x, TexCoord.y, coordZ, 1.0) * mtx;
1266
+ uint L = texture(intensityVol, vx.xyz).r;
1267
+ vx = vec4(TexCoord.x, TexCoord.y+xyzFrac.y, coordZ, 1.0) * mtx;
1268
+ uint A = texture(intensityVol, vx.xyz).r;
1269
+ vx = vec4(TexCoord.x, TexCoord.y-xyzFrac.y, coordZ, 1.0) * mtx;
1270
+ uint P = texture(intensityVol, vx.xyz).r;
1271
+ vx = vec4(TexCoord.x, TexCoord.y, coordZ+xyzFrac.z, 1.0) * mtx;
1272
+ uint S = texture(intensityVol, vx.xyz).r;
1273
+ vx = vec4(TexCoord.x, TexCoord.y, coordZ-xyzFrac.z, 1.0) * mtx;
1274
+ uint I = texture(intensityVol, vx.xyz).r;
1275
+ if ((idx == R) && (idx == L) && (idx == A) && (idx == P) && (idx == S) && (idx == I))
1276
+ return;
1277
+ }
1278
+ idx = ((idx - uint(1)) % uint(100))+uint(1);
1279
+ float fx = (float(idx)+0.5) / 256.0;
1280
+ float nlayer = float(textureSize(colormap, 0).y) * 0.5; //0.5 as both each layer has positive and negative color slot
1281
+ float y = (2.0 * layer + 1.0)/(4.0 * nlayer);
1282
+ //float y = (2.0 * layer + 1.0)/(4.0 * numLayers);
1283
+ FragColor = texture(colormap, vec2(fx, y)).rgba;
1284
+ //FragColor.a *= opacity;
1285
+ FragColor.a = opacity;
1286
+ return;
1287
+
1288
+ if (layer < 2.0) return;
1289
+ vec2 texXY = TexCoord.xy*0.5 +vec2(0.5,0.5);
1290
+ vec4 prevColor = texture(blend3D, vec3(texXY, coordZ));
1291
+ // https://en.wikipedia.org/wiki/Alpha_compositing
1292
+ float aout = FragColor.a + (1.0 - FragColor.a) * prevColor.a;
1293
+ if (aout <= 0.0) return;
1294
+ FragColor.rgb = ((FragColor.rgb * FragColor.a) + (prevColor.rgb * prevColor.a * (1.0 - FragColor.a))) / aout;
1295
+ FragColor.a = aout;
1300
1296
  }`;
1301
1297
  var fragOrientShader = `#line 309
1302
1298
  precision highp int;
@@ -1317,40 +1313,40 @@ uniform highp sampler3D modulationVol;
1317
1313
  uniform float opacity;
1318
1314
  uniform mat4 mtx;
1319
1315
  void main(void) {
1320
- vec4 vx = vec4(TexCoord.xy, coordZ, 1.0) * mtx;
1321
- float f = (scl_slope * float(texture(intensityVol, vx.xyz).r)) + scl_inter;
1322
- bool isNegative = (f < 0.0);
1323
- float r = max(0.00001, abs(cal_max - cal_min));
1324
- float mn = min(cal_min, cal_max);
1325
- float txl = mix(0.0, 1.0, (f - mn) / r);
1326
- //https://stackoverflow.com/questions/5879403/opengl-texture-coordinates-in-pixel-space
1327
- float nlayer = float(textureSize(colormap, 0).y) * 0.5; //0.5 as both each layer has positive and negative color slot
1328
- float y = (2.0 * layer + 1.0)/(4.0 * nlayer);
1329
- FragColor = texture(colormap, vec2(txl, y)).rgba;
1330
- if (isNegative) {
1331
- y = (2.0 * layer + nlayer + nlayer + 1.0)/(4.0 * nlayer);
1332
- //select texels at positions 0 and 1 of lookup table:
1333
- vec4 v0 = texture(colormap, vec2(0.5/256.0, y));
1334
- vec4 v1 = texture(colormap, vec2(1.5/256.0, y));
1335
- txl = mix(0.0, 1.0, (- f - mn) / r);
1336
- //detect bogus color: negative color slot not used than
1337
- // v0 = 1,1,1,0 and v1 = 0,0,0,1
1338
- if ((v0.r != 1.0) || (v0.a != 0.0) || (v1.r != 0.0) || (v1.a != 1.0))
1339
- FragColor = texture(colormap, vec2(txl, y));
1340
- }
1341
- if (layer > 0.7)
1342
- FragColor.a = step(0.00001, FragColor.a);
1343
- if (modulation > 0)
1344
- FragColor.rgb *= texture(modulationVol, vx.xyz).r;
1345
- FragColor.a *= opacity;
1346
- if (layer < 1.0) return;
1347
- vec2 texXY = TexCoord.xy*0.5 +vec2(0.5,0.5);
1348
- vec4 prevColor = texture(blend3D, vec3(texXY, coordZ));
1349
- // https://en.wikipedia.org/wiki/Alpha_compositing
1350
- float aout = FragColor.a + (1.0 - FragColor.a) * prevColor.a;
1351
- if (aout <= 0.0) return;
1352
- FragColor.rgb = ((FragColor.rgb * FragColor.a) + (prevColor.rgb * prevColor.a * (1.0 - FragColor.a))) / aout;
1353
- FragColor.a = aout;
1316
+ vec4 vx = vec4(TexCoord.xy, coordZ, 1.0) * mtx;
1317
+ float f = (scl_slope * float(texture(intensityVol, vx.xyz).r)) + scl_inter;
1318
+ bool isNegative = (f < 0.0);
1319
+ float r = max(0.00001, abs(cal_max - cal_min));
1320
+ float mn = min(cal_min, cal_max);
1321
+ float txl = mix(0.0, 1.0, (f - mn) / r);
1322
+ //https://stackoverflow.com/questions/5879403/opengl-texture-coordinates-in-pixel-space
1323
+ float nlayer = float(textureSize(colormap, 0).y) * 0.5; //0.5 as both each layer has positive and negative color slot
1324
+ float y = (2.0 * layer + 1.0)/(4.0 * nlayer);
1325
+ FragColor = texture(colormap, vec2(txl, y)).rgba;
1326
+ if (isNegative) {
1327
+ y = (2.0 * layer + nlayer + nlayer + 1.0)/(4.0 * nlayer);
1328
+ //select texels at positions 0 and 1 of lookup table:
1329
+ vec4 v0 = texture(colormap, vec2(0.5/256.0, y));
1330
+ vec4 v1 = texture(colormap, vec2(1.5/256.0, y));
1331
+ txl = mix(0.0, 1.0, (- f - mn) / r);
1332
+ //detect bogus color: negative color slot not used than
1333
+ // v0 = 1,1,1,0 and v1 = 0,0,0,1
1334
+ if ((v0.r != 1.0) || (v0.a != 0.0) || (v1.r != 0.0) || (v1.a != 1.0))
1335
+ FragColor = texture(colormap, vec2(txl, y));
1336
+ }
1337
+ if (layer > 0.7)
1338
+ FragColor.a = step(0.00001, FragColor.a);
1339
+ if (modulation > 0)
1340
+ FragColor.rgb *= texture(modulationVol, vx.xyz).r;
1341
+ FragColor.a *= opacity;
1342
+ if (layer < 1.0) return;
1343
+ vec2 texXY = TexCoord.xy*0.5 +vec2(0.5,0.5);
1344
+ vec4 prevColor = texture(blend3D, vec3(texXY, coordZ));
1345
+ // https://en.wikipedia.org/wiki/Alpha_compositing
1346
+ float aout = FragColor.a + (1.0 - FragColor.a) * prevColor.a;
1347
+ if (aout <= 0.0) return;
1348
+ FragColor.rgb = ((FragColor.rgb * FragColor.a) + (prevColor.rgb * prevColor.a * (1.0 - FragColor.a))) / aout;
1349
+ FragColor.a = aout;
1354
1350
  }`;
1355
1351
  var fragRGBOrientShader = `#line 309
1356
1352
  precision highp int;
@@ -1372,14 +1368,14 @@ uniform bool hasAlpha;
1372
1368
  uniform int modulation;
1373
1369
  uniform highp sampler3D modulationVol;
1374
1370
  void main(void) {
1375
- vec4 vx = vec4(TexCoord.xy, coordZ, 1.0) * mtx;
1376
- uvec4 aColor = texture(intensityVol, vx.xyz);
1377
- FragColor = vec4(float(aColor.r) / 255.0, float(aColor.g) / 255.0, float(aColor.b) / 255.0, float(aColor.a) / 255.0);
1378
- if (modulation > 0)
1379
- FragColor.rgb *= texture(modulationVol, vx.xyz).r;
1380
- if (!hasAlpha)
1381
- FragColor.a = (FragColor.r * 0.21 + FragColor.g * 0.72 + FragColor.b * 0.07);
1382
- FragColor.a *= opacity;
1371
+ vec4 vx = vec4(TexCoord.xy, coordZ, 1.0) * mtx;
1372
+ uvec4 aColor = texture(intensityVol, vx.xyz);
1373
+ FragColor = vec4(float(aColor.r) / 255.0, float(aColor.g) / 255.0, float(aColor.b) / 255.0, float(aColor.a) / 255.0);
1374
+ if (modulation > 0)
1375
+ FragColor.rgb *= texture(modulationVol, vx.xyz).r;
1376
+ if (!hasAlpha)
1377
+ FragColor.a = (FragColor.r * 0.21 + FragColor.g * 0.72 + FragColor.b * 0.07);
1378
+ FragColor.a *= opacity;
1383
1379
  }`;
1384
1380
  var vertPassThroughShader = `#version 300 es
1385
1381
  #line 283
@@ -1388,8 +1384,8 @@ precision highp float;
1388
1384
  in vec3 vPos;
1389
1385
  out vec2 TexCoord;
1390
1386
  void main() {
1391
- TexCoord = vPos.xy;
1392
- gl_Position = vec4(vPos.x, vPos.y, 0.0, 1.0);
1387
+ TexCoord = vPos.xy;
1388
+ gl_Position = vec4(vPos.x, vPos.y, 0.0, 1.0);
1393
1389
  }`;
1394
1390
  var fragPassThroughShader = `#version 300 es
1395
1391
  precision highp int;
@@ -1408,70 +1404,70 @@ precision highp float;
1408
1404
  in vec3 vPos;
1409
1405
  out vec2 TexCoord;
1410
1406
  void main() {
1411
- TexCoord = vPos.xy;
1412
- gl_Position = vec4((vPos.x - 0.5) * 2.0, (vPos.y - 0.5) * 2.0, 0.0, 1.0);
1407
+ TexCoord = vPos.xy;
1408
+ gl_Position = vec4((vPos.x - 0.5) * 2.0, (vPos.y - 0.5) * 2.0, 0.0, 1.0);
1413
1409
  }`;
1414
1410
  var fragGrowCutShader = `#version 300 es
1415
1411
  #line 742
1416
- precision highp float;
1417
- precision highp int;
1418
- precision highp isampler3D;
1419
- layout(location = 0) out int label;
1420
- layout(location = 1) out int strength;
1421
- in vec2 TexCoord;
1422
- uniform int finalPass;
1423
- uniform float coordZ;
1424
- uniform lowp sampler3D in3D;
1425
- uniform highp isampler3D inputTexture0; // background
1426
- uniform highp isampler3D inputTexture1; // label
1427
- uniform highp isampler3D inputTexture2; // strength
1412
+ precision highp float;
1413
+ precision highp int;
1414
+ precision highp isampler3D;
1415
+ layout(location = 0) out int label;
1416
+ layout(location = 1) out int strength;
1417
+ in vec2 TexCoord;
1418
+ uniform int finalPass;
1419
+ uniform float coordZ;
1420
+ uniform lowp sampler3D in3D;
1421
+ uniform highp isampler3D inputTexture0; // background
1422
+ uniform highp isampler3D inputTexture1; // label
1423
+ uniform highp isampler3D inputTexture2; // strength
1428
1424
  void main(void) {
1429
- vec3 interpolatedTextureCoordinate = vec3(TexCoord.xy, coordZ);
1430
- ivec3 size = textureSize(inputTexture0, 0);
1431
- ivec3 texelIndex = ivec3(floor(interpolatedTextureCoordinate * vec3(size)));
1432
- int background = texelFetch(inputTexture0, texelIndex, 0).r;
1433
- label = texelFetch(inputTexture1, texelIndex, 0).r;
1434
- strength = texelFetch(inputTexture2, texelIndex, 0).r;
1435
- for (int k = -1; k <= 1; k++) {
1436
- for (int j = -1; j <= 1; j++) {
1437
- for (int i = -1; i <= 1; i++) {
1438
- if (i != 0 && j != 0 && k != 0) {
1439
- ivec3 neighborIndex = texelIndex + ivec3(i,j,k);
1440
- int neighborBackground = texelFetch(inputTexture0, neighborIndex, 0).r;
1441
- int neighborStrength = texelFetch(inputTexture2, neighborIndex, 0).r;
1442
- int strengthCost = abs(neighborBackground - background);
1443
- int takeoverStrength = neighborStrength - strengthCost;
1444
- if (takeoverStrength > strength) {
1445
- strength = takeoverStrength;
1446
- label = texelFetch(inputTexture1, neighborIndex, 0).r;
1447
- }
1448
- }
1449
- }
1450
- }
1451
- }
1452
- if (finalPass < 1)
1453
- return;
1454
- int ok = 1;
1455
- ivec4 labelCount = ivec4(0,0,0,0);
1456
- for (int k = -1; k <= 1; k++)
1457
- for (int j = -1; j <= 1; j++)
1458
- for (int i = -1; i <= 1; i++) {
1459
- ivec3 neighborIndex = texelIndex + ivec3(i,j,k);
1460
- int ilabel = texelFetch(inputTexture1, neighborIndex, 0).r;
1461
- if ((ilabel < 0) || (ilabel > 3))
1462
- ok = 0;
1463
- else
1464
- labelCount[ilabel]++;
1465
- }
1466
- if (ok != 1) {
1467
- return;
1468
- }
1469
- int maxIdx = 0;
1470
- for (int i = 1; i < 4; i++) {
1471
- if (labelCount[i] > labelCount[maxIdx])
1472
- maxIdx = i;
1473
- }
1474
- label = maxIdx;
1425
+ vec3 interpolatedTextureCoordinate = vec3(TexCoord.xy, coordZ);
1426
+ ivec3 size = textureSize(inputTexture0, 0);
1427
+ ivec3 texelIndex = ivec3(floor(interpolatedTextureCoordinate * vec3(size)));
1428
+ int background = texelFetch(inputTexture0, texelIndex, 0).r;
1429
+ label = texelFetch(inputTexture1, texelIndex, 0).r;
1430
+ strength = texelFetch(inputTexture2, texelIndex, 0).r;
1431
+ for (int k = -1; k <= 1; k++) {
1432
+ for (int j = -1; j <= 1; j++) {
1433
+ for (int i = -1; i <= 1; i++) {
1434
+ if (i != 0 && j != 0 && k != 0) {
1435
+ ivec3 neighborIndex = texelIndex + ivec3(i,j,k);
1436
+ int neighborBackground = texelFetch(inputTexture0, neighborIndex, 0).r;
1437
+ int neighborStrength = texelFetch(inputTexture2, neighborIndex, 0).r;
1438
+ int strengthCost = abs(neighborBackground - background);
1439
+ int takeoverStrength = neighborStrength - strengthCost;
1440
+ if (takeoverStrength > strength) {
1441
+ strength = takeoverStrength;
1442
+ label = texelFetch(inputTexture1, neighborIndex, 0).r;
1443
+ }
1444
+ }
1445
+ }
1446
+ }
1447
+ }
1448
+ if (finalPass < 1)
1449
+ return;
1450
+ int ok = 1;
1451
+ ivec4 labelCount = ivec4(0,0,0,0);
1452
+ for (int k = -1; k <= 1; k++)
1453
+ for (int j = -1; j <= 1; j++)
1454
+ for (int i = -1; i <= 1; i++) {
1455
+ ivec3 neighborIndex = texelIndex + ivec3(i,j,k);
1456
+ int ilabel = texelFetch(inputTexture1, neighborIndex, 0).r;
1457
+ if ((ilabel < 0) || (ilabel > 3))
1458
+ ok = 0;
1459
+ else
1460
+ labelCount[ilabel]++;
1461
+ }
1462
+ if (ok != 1) {
1463
+ return;
1464
+ }
1465
+ int maxIdx = 0;
1466
+ for (int i = 1; i < 4; i++) {
1467
+ if (labelCount[i] > labelCount[maxIdx])
1468
+ maxIdx = i;
1469
+ }
1470
+ label = maxIdx;
1475
1471
  }`;
1476
1472
  var vertSurfaceShader = `#version 300 es
1477
1473
  layout(location=0) in vec3 pos;
@@ -1529,7 +1525,6 @@ precision highp float;
1529
1525
  uniform float opacity;
1530
1526
  out vec4 color;
1531
1527
  vec4 packFloatToVec4i(const float value) {
1532
- //this Papaya function uses BSD 3-clause license Copyright (c) 2012-2015, RII-UTHSCSA
1533
1528
  const vec4 bitSh = vec4(256.0*256.0*256.0, 256.0*256.0, 256.0, 1.0);
1534
1529
  const vec4 bitMsk = vec4(0.0, 1.0/256.0, 1.0/256.0, 1.0/256.0);
1535
1530
  vec4 res = fract(value * bitSh);
@@ -1560,7 +1555,7 @@ void main() {
1560
1555
  vec3 lightPosition = vec3(0.0, 10.0, -5.0);
1561
1556
  vec3 l = normalize(lightPosition);
1562
1557
  float df = max(0.0, dot(n, l));
1563
- float sf = pow(max(dot(reflect(l, n), r), 0.0), shininess);
1558
+ float sf = pow(max(dot(reflect(l, n), r), 0.0), shininess);
1564
1559
  const float A = 0.1;
1565
1560
  const float B = 0.3;
1566
1561
  const float C = 0.6;
@@ -1602,11 +1597,10 @@ void main() {
1602
1597
  vec3 l = normalize(lightPosition);
1603
1598
  float lightNormDot = dot(n, l);
1604
1599
  float view = abs(dot(n,r)); //with respect to viewer
1605
-
1606
1600
  if (PenWidth < view) discard;
1607
1601
  vec3 a = vClr.rgb * ambient;
1608
1602
  vec3 d = max(lightNormDot, 0.0) * vClr.rgb * diffuse;
1609
- float s = specular * pow(max(dot(reflect(l, n), r), 0.0), shininess);
1603
+ float s = specular * pow(max(dot(reflect(l, n), r), 0.0), shininess);
1610
1604
  color.rgb = a + d + s;
1611
1605
  color.a = opacity;
1612
1606
  }`;
@@ -1629,7 +1623,7 @@ void main() {
1629
1623
  float lightNormDot = dot(n, l);
1630
1624
  vec3 a = vClr.rgb * ambient;
1631
1625
  vec3 d = max(lightNormDot, 0.0) * vClr.rgb * diffuse;
1632
- float s = specular * pow(max(dot(reflect(l, n), r), 0.0), shininess);
1626
+ float s = specular * pow(max(dot(reflect(l, n), r), 0.0), shininess);
1633
1627
  color = vec4(a + d + s, opacity);
1634
1628
  }`;
1635
1629
  var fragMeshMatteShader = `#version 300 es
@@ -1640,7 +1634,6 @@ in vec4 vClr;
1640
1634
  in vec3 vN, vL, vV;
1641
1635
  out vec4 color;
1642
1636
  void main() {
1643
- vec3 r = vec3(0.0, 0.0, 1.0); //rayDir: for orthographic projections moving in Z direction (no need for normal matrix)
1644
1637
  float ambient = 0.35;
1645
1638
  float diffuse = 0.6;
1646
1639
  vec3 n = normalize(vN);
@@ -1675,7 +1668,7 @@ void main() {
1675
1668
  vec3 a = vClr.rgb * ambient;
1676
1669
  a *= mix(downClr, upClr, ax);
1677
1670
  vec3 d = max(lightNormDot, 0.0) * vClr.rgb * diffuse;
1678
- float s = specular * pow(max(dot(reflect(l, n), r), 0.0), shininess);
1671
+ float s = specular * pow(max(dot(reflect(l, n), r), 0.0), shininess);
1679
1672
  color = vec4(a + d + s, opacity);
1680
1673
  }`;
1681
1674
  var fragMeshShaderSHBlue = `#version 300 es
@@ -1707,8 +1700,7 @@ const vec3 L21 = vec3(-0.0572703, -0.0502192, -0.0363410);
1707
1700
  const vec3 L22 = vec3( 0.0203348, -0.0044201, -0.0452180);
1708
1701
  vec3 SH(vec3 vNormal) {
1709
1702
  vNormal = vec3(vNormal.x,vNormal.z,vNormal.y);
1710
- //vNormal = vec3(vNormal.x,vNormal.z,vNormal.y);
1711
- vec3 diffuseColor = C1 * L22 * (vNormal.x * vNormal.x - vNormal.y * vNormal.y) +
1703
+ vec3 diffuseColor = C1 * L22 * (vNormal.x * vNormal.x - vNormal.y * vNormal.y) +
1712
1704
  C3 * L20 * vNormal.z * vNormal.z +
1713
1705
  C4 * L00 -
1714
1706
  C5 * L20 +
@@ -1729,20 +1721,11 @@ void main() {
1729
1721
  vec3 n = normalize(vN);
1730
1722
  vec3 lightPosition = vec3(0.0, 10.0, -5.0);
1731
1723
  vec3 l = normalize(lightPosition);
1732
- float s = specular * pow(max(dot(reflect(l, n), r), 0.0), shininess);
1724
+ float s = specular * pow(max(dot(reflect(l, n), r), 0.0), shininess);
1733
1725
  vec3 a = vClr.rgb * ambient;
1734
1726
  vec3 d = vClr.rgb * diffuse * SH(-reflect(n, vec3(l.x, l.y, -l.z)) );
1735
1727
  color = vec4(a + d + s, opacity);
1736
1728
  }`;
1737
- var fragDepthPickingShader = `#version 300 es
1738
- precision highp int;
1739
- precision highp float;
1740
- uniform int id;
1741
- in vec3 vColor;
1742
- out vec4 color;
1743
- void main() {
1744
- color = vec4(vColor, float(id & 255) / 255.0);
1745
- }`;
1746
1729
  var fragVolumePickingShader = `#version 300 es
1747
1730
  #line 506
1748
1731
  //precision highp int;
@@ -1753,12 +1736,17 @@ uniform vec3 texVox;
1753
1736
  uniform vec4 clipPlane;
1754
1737
  uniform highp sampler3D volume, overlay;
1755
1738
  uniform float overlays;
1756
- uniform int id;
1739
+ uniform mat4 matRAS;
1740
+ uniform mat4 mvpMtx;
1741
+ uniform float drawOpacity;
1742
+ uniform highp sampler3D drawing;
1757
1743
  in vec3 vColor;
1758
1744
  out vec4 fColor;
1759
1745
  ` + kRenderFunc + `
1760
1746
  void main() {
1747
+ int id = 254;
1761
1748
  vec3 start = vColor;
1749
+ gl_FragDepth = 0.0;
1762
1750
  fColor = vec4(0.0, 0.0, 0.0, 0.0); //assume no hit: ID = 0
1763
1751
  float fid = float(id & 255)/ 255.0;
1764
1752
  vec3 backPosition = GetBackPosition(start);
@@ -1782,6 +1770,7 @@ void main() {
1782
1770
  float val = texture(volume, samplePos.xyz).a;
1783
1771
  if (val > 0.01) {
1784
1772
  fColor = vec4(samplePos.rgb, fid);
1773
+ gl_FragDepth = frac2ndc(samplePos.xyz);
1785
1774
  break;
1786
1775
  }
1787
1776
  samplePos += deltaDirFast; //advance ray position
@@ -1798,6 +1787,7 @@ void main() {
1798
1787
  float val = texture(overlay, samplePos.xyz).a;
1799
1788
  if (val > 0.01) {
1800
1789
  fColor = vec4(samplePos.rgb, fid);
1790
+ gl_FragDepth = frac2ndc(samplePos.xyz);
1801
1791
  return;
1802
1792
  }
1803
1793
  samplePos += deltaDirFast; //advance ray position
@@ -1805,6 +1795,31 @@ void main() {
1805
1795
  //if (fColor.a == 0.0) discard; //no hit in either background or overlays
1806
1796
  //you only get here if there is a hit with the background that is closer than any overlay
1807
1797
  }`;
1798
+ var vertOrientCubeShader = `#version 300 es
1799
+ // an attribute is an input (in) to a vertex shader.
1800
+ // It will receive data from a buffer
1801
+ layout(location=0) in vec3 a_position;
1802
+ layout(location=1) in vec3 a_color;
1803
+ // A matrix to transform the positions by
1804
+ uniform mat4 u_matrix;
1805
+ out vec3 vColor;
1806
+ // all shaders have a main function
1807
+ void main() {
1808
+ // Multiply the position by the matrix.
1809
+ vec4 pos = vec4(a_position, 1.0);
1810
+ gl_Position = u_matrix * vec4(pos);
1811
+ vColor = a_color;
1812
+ }
1813
+ `;
1814
+ var fragOrientCubeShader = `#version 300 es
1815
+ precision highp float;
1816
+ uniform vec4 u_color;
1817
+ in vec3 vColor;
1818
+ // we need to declare an output for the fragment shader
1819
+ out vec4 outColor;
1820
+ void main() {
1821
+ outColor = vec4(vColor, 1.0);
1822
+ }`;
1808
1823
  /*! *****************************************************************************
1809
1824
  Copyright (c) Microsoft Corporation.
1810
1825
 
@@ -2844,6 +2859,1016 @@ function interval(period, scheduler) {
2844
2859
  }
2845
2860
  return timer(period, period, scheduler);
2846
2861
  }
2862
+ const orientCube = new Float32Array([
2863
+ -1,
2864
+ -1,
2865
+ -1,
2866
+ 0.28,
2867
+ 0.28,
2868
+ 0.28,
2869
+ -1,
2870
+ -1,
2871
+ -1,
2872
+ 0.28,
2873
+ 0.28,
2874
+ 0.28,
2875
+ -1,
2876
+ 1,
2877
+ -1,
2878
+ 0.28,
2879
+ 0.28,
2880
+ 0.28,
2881
+ 1,
2882
+ -1,
2883
+ -1,
2884
+ 0.28,
2885
+ 0.28,
2886
+ 0.28,
2887
+ 1,
2888
+ 1,
2889
+ -1,
2890
+ 0.28,
2891
+ 0.28,
2892
+ 0.28,
2893
+ 1,
2894
+ 1,
2895
+ -1,
2896
+ 0.28,
2897
+ 0.28,
2898
+ 0.28,
2899
+ -1,
2900
+ -1,
2901
+ 1,
2902
+ 0.8,
2903
+ 0.8,
2904
+ 0.8,
2905
+ -1,
2906
+ -1,
2907
+ 1,
2908
+ 0.8,
2909
+ 0.8,
2910
+ 0.8,
2911
+ 1,
2912
+ -1,
2913
+ 1,
2914
+ 0.8,
2915
+ 0.8,
2916
+ 0.8,
2917
+ -1,
2918
+ 1,
2919
+ 1,
2920
+ 0.8,
2921
+ 0.8,
2922
+ 0.8,
2923
+ 1,
2924
+ 1,
2925
+ 1,
2926
+ 0.8,
2927
+ 0.8,
2928
+ 0.8,
2929
+ 1,
2930
+ 1,
2931
+ 1,
2932
+ 0.8,
2933
+ 0.8,
2934
+ 0.8,
2935
+ -1,
2936
+ 1,
2937
+ -1,
2938
+ 0,
2939
+ 0,
2940
+ 0.74,
2941
+ -1,
2942
+ 1,
2943
+ -1,
2944
+ 0,
2945
+ 0,
2946
+ 0.74,
2947
+ -1,
2948
+ 1,
2949
+ 1,
2950
+ 0,
2951
+ 0,
2952
+ 0.74,
2953
+ 1,
2954
+ 1,
2955
+ -1,
2956
+ 0,
2957
+ 0,
2958
+ 0.74,
2959
+ 1,
2960
+ 1,
2961
+ 1,
2962
+ 0,
2963
+ 0,
2964
+ 0.74,
2965
+ 1,
2966
+ 1,
2967
+ 1,
2968
+ 0,
2969
+ 0,
2970
+ 0.74,
2971
+ -1,
2972
+ -1,
2973
+ -1,
2974
+ 0.42,
2975
+ 0,
2976
+ 0.42,
2977
+ -1,
2978
+ -1,
2979
+ -1,
2980
+ 0.42,
2981
+ 0,
2982
+ 0.42,
2983
+ 1,
2984
+ -1,
2985
+ -1,
2986
+ 0.42,
2987
+ 0,
2988
+ 0.42,
2989
+ -1,
2990
+ -1,
2991
+ 1,
2992
+ 0.42,
2993
+ 0,
2994
+ 0.42,
2995
+ 1,
2996
+ -1,
2997
+ 1,
2998
+ 0.42,
2999
+ 0,
3000
+ 0.42,
3001
+ 1,
3002
+ -1,
3003
+ 1,
3004
+ 0.42,
3005
+ 0,
3006
+ 0.42,
3007
+ -1,
3008
+ -1,
3009
+ -1,
3010
+ 0.64,
3011
+ 0,
3012
+ 0,
3013
+ -1,
3014
+ -1,
3015
+ -1,
3016
+ 0.64,
3017
+ 0,
3018
+ 0,
3019
+ -1,
3020
+ -1,
3021
+ 1,
3022
+ 0.64,
3023
+ 0,
3024
+ 0,
3025
+ -1,
3026
+ 1,
3027
+ -1,
3028
+ 0.64,
3029
+ 0,
3030
+ 0,
3031
+ -1,
3032
+ 1,
3033
+ 1,
3034
+ 0.64,
3035
+ 0,
3036
+ 0,
3037
+ -1,
3038
+ 1,
3039
+ 1,
3040
+ 0.64,
3041
+ 0,
3042
+ 0,
3043
+ 1,
3044
+ -1,
3045
+ -1,
3046
+ 0,
3047
+ 0.5,
3048
+ 0,
3049
+ 1,
3050
+ -1,
3051
+ -1,
3052
+ 0,
3053
+ 0.5,
3054
+ 0,
3055
+ 1,
3056
+ 1,
3057
+ -1,
3058
+ 0,
3059
+ 0.5,
3060
+ 0,
3061
+ 1,
3062
+ -1,
3063
+ 1,
3064
+ 0,
3065
+ 0.5,
3066
+ 0,
3067
+ 1,
3068
+ 1,
3069
+ 1,
3070
+ 0,
3071
+ 0.5,
3072
+ 0,
3073
+ 1,
3074
+ 1,
3075
+ 1,
3076
+ 0,
3077
+ 0.5,
3078
+ 0,
3079
+ -0.45,
3080
+ 1,
3081
+ -0.8,
3082
+ 0,
3083
+ 0,
3084
+ 0,
3085
+ -0.45,
3086
+ 1,
3087
+ -0.8,
3088
+ 0,
3089
+ 0,
3090
+ 0,
3091
+ -0.45,
3092
+ 1,
3093
+ 0.8,
3094
+ 0,
3095
+ 0,
3096
+ 0,
3097
+ -0.25,
3098
+ 1,
3099
+ -0.8,
3100
+ 0,
3101
+ 0,
3102
+ 0,
3103
+ -0.25,
3104
+ 1,
3105
+ 0.8,
3106
+ 0,
3107
+ 0,
3108
+ 0,
3109
+ -0.25,
3110
+ 1,
3111
+ 0.8,
3112
+ 0,
3113
+ 0,
3114
+ 0,
3115
+ -0.25,
3116
+ 1,
3117
+ 0.6,
3118
+ 0,
3119
+ 0,
3120
+ 0,
3121
+ -0.25,
3122
+ 1,
3123
+ 0.6,
3124
+ 0,
3125
+ 0,
3126
+ 0,
3127
+ -0.25,
3128
+ 1,
3129
+ 0.8,
3130
+ 0,
3131
+ 0,
3132
+ 0,
3133
+ 0.45,
3134
+ 1,
3135
+ 0.6,
3136
+ 0,
3137
+ 0,
3138
+ 0,
3139
+ 0.25,
3140
+ 1,
3141
+ 0.8,
3142
+ 0,
3143
+ 0,
3144
+ 0,
3145
+ 0.25,
3146
+ 1,
3147
+ 0.8,
3148
+ 0,
3149
+ 0,
3150
+ 0,
3151
+ 0.25,
3152
+ 1,
3153
+ 0.1,
3154
+ 0,
3155
+ 0,
3156
+ 0,
3157
+ 0.25,
3158
+ 1,
3159
+ 0.1,
3160
+ 0,
3161
+ 0,
3162
+ 0,
3163
+ 0.25,
3164
+ 1,
3165
+ 0.6,
3166
+ 0,
3167
+ 0,
3168
+ 0,
3169
+ 0.45,
3170
+ 1,
3171
+ 0.1,
3172
+ 0,
3173
+ 0,
3174
+ 0,
3175
+ 0.45,
3176
+ 1,
3177
+ 0.6,
3178
+ 0,
3179
+ 0,
3180
+ 0,
3181
+ 0.45,
3182
+ 1,
3183
+ 0.6,
3184
+ 0,
3185
+ 0,
3186
+ 0,
3187
+ -0.25,
3188
+ 1,
3189
+ -0.1,
3190
+ 0,
3191
+ 0,
3192
+ 0,
3193
+ -0.25,
3194
+ 1,
3195
+ -0.1,
3196
+ 0,
3197
+ 0,
3198
+ 0,
3199
+ -0.25,
3200
+ 1,
3201
+ 0.1,
3202
+ 0,
3203
+ 0,
3204
+ 0,
3205
+ 0.25,
3206
+ 1,
3207
+ -0.1,
3208
+ 0,
3209
+ 0,
3210
+ 0,
3211
+ 0.45,
3212
+ 1,
3213
+ 0.1,
3214
+ 0,
3215
+ 0,
3216
+ 0,
3217
+ 0.45,
3218
+ 1,
3219
+ 0.1,
3220
+ 0,
3221
+ 0,
3222
+ 0,
3223
+ 0.45,
3224
+ -1,
3225
+ -0.8,
3226
+ 0,
3227
+ 0,
3228
+ 0,
3229
+ 0.45,
3230
+ -1,
3231
+ -0.8,
3232
+ 0,
3233
+ 0,
3234
+ 0,
3235
+ 0.05,
3236
+ -1,
3237
+ 0.8,
3238
+ 0,
3239
+ 0,
3240
+ 0,
3241
+ 0.25,
3242
+ -1,
3243
+ -0.8,
3244
+ 0,
3245
+ 0,
3246
+ 0,
3247
+ -0.15,
3248
+ -1,
3249
+ 0.8,
3250
+ 0,
3251
+ 0,
3252
+ 0,
3253
+ -0.15,
3254
+ -1,
3255
+ 0.8,
3256
+ 0,
3257
+ 0,
3258
+ 0,
3259
+ -0.25,
3260
+ -1,
3261
+ -0.8,
3262
+ 0,
3263
+ 0,
3264
+ 0,
3265
+ -0.25,
3266
+ -1,
3267
+ -0.8,
3268
+ 0,
3269
+ 0,
3270
+ 0,
3271
+ 0.05,
3272
+ -1,
3273
+ 0.8,
3274
+ 0,
3275
+ 0,
3276
+ 0,
3277
+ -0.45,
3278
+ -1,
3279
+ -0.8,
3280
+ 0,
3281
+ 0,
3282
+ 0,
3283
+ -0.15,
3284
+ -1,
3285
+ 0.8,
3286
+ 0,
3287
+ 0,
3288
+ 0,
3289
+ -0.15,
3290
+ -1,
3291
+ 0.8,
3292
+ 0,
3293
+ 0,
3294
+ 0,
3295
+ 0.13,
3296
+ -1,
3297
+ -0.3,
3298
+ 0,
3299
+ 0,
3300
+ 0,
3301
+ 0.13,
3302
+ -1,
3303
+ -0.3,
3304
+ 0,
3305
+ 0,
3306
+ 0,
3307
+ 0.07,
3308
+ -1,
3309
+ -0.1,
3310
+ 0,
3311
+ 0,
3312
+ 0,
3313
+ -0.33,
3314
+ -1,
3315
+ -0.3,
3316
+ 0,
3317
+ 0,
3318
+ 0,
3319
+ -0.27,
3320
+ -1,
3321
+ -0.1,
3322
+ 0,
3323
+ 0,
3324
+ 0,
3325
+ -0.27,
3326
+ -1,
3327
+ -0.1,
3328
+ 0,
3329
+ 0,
3330
+ 0,
3331
+ -0.45,
3332
+ 0.6,
3333
+ 1,
3334
+ 0,
3335
+ 0,
3336
+ 0,
3337
+ -0.45,
3338
+ 0.6,
3339
+ 1,
3340
+ 0,
3341
+ 0,
3342
+ 0,
3343
+ -0.45,
3344
+ 0.4,
3345
+ 1,
3346
+ 0,
3347
+ 0,
3348
+ 0,
3349
+ -0.25,
3350
+ 0.8,
3351
+ 1,
3352
+ 0,
3353
+ 0,
3354
+ 0,
3355
+ -0.25,
3356
+ 0.4,
3357
+ 1,
3358
+ 0,
3359
+ 0,
3360
+ 0,
3361
+ -0.25,
3362
+ 0.4,
3363
+ 1,
3364
+ 0,
3365
+ 0,
3366
+ 0,
3367
+ -0.25,
3368
+ 0.8,
3369
+ 1,
3370
+ 0,
3371
+ 0,
3372
+ 0,
3373
+ -0.25,
3374
+ 0.8,
3375
+ 1,
3376
+ 0,
3377
+ 0,
3378
+ 0,
3379
+ -0.25,
3380
+ 0.6,
3381
+ 1,
3382
+ 0,
3383
+ 0,
3384
+ 0,
3385
+ 0.25,
3386
+ 0.8,
3387
+ 1,
3388
+ 0,
3389
+ 0,
3390
+ 0,
3391
+ 0.45,
3392
+ 0.6,
3393
+ 1,
3394
+ 0,
3395
+ 0,
3396
+ 0,
3397
+ 0.45,
3398
+ 0.6,
3399
+ 1,
3400
+ 0,
3401
+ 0,
3402
+ 0,
3403
+ 0.25,
3404
+ 0.8,
3405
+ 1,
3406
+ 0,
3407
+ 0,
3408
+ 0,
3409
+ 0.25,
3410
+ 0.8,
3411
+ 1,
3412
+ 0,
3413
+ 0,
3414
+ 0,
3415
+ 0.25,
3416
+ -0.1,
3417
+ 1,
3418
+ 0,
3419
+ 0,
3420
+ 0,
3421
+ 0.45,
3422
+ 0.6,
3423
+ 1,
3424
+ 0,
3425
+ 0,
3426
+ 0,
3427
+ 0.45,
3428
+ 0.1,
3429
+ 1,
3430
+ 0,
3431
+ 0,
3432
+ 0,
3433
+ 0.45,
3434
+ 0.1,
3435
+ 1,
3436
+ 0,
3437
+ 0,
3438
+ 0,
3439
+ -0.25,
3440
+ 0.1,
3441
+ 1,
3442
+ 0,
3443
+ 0,
3444
+ 0,
3445
+ -0.25,
3446
+ 0.1,
3447
+ 1,
3448
+ 0,
3449
+ 0,
3450
+ 0,
3451
+ -0.45,
3452
+ -0.1,
3453
+ 1,
3454
+ 0,
3455
+ 0,
3456
+ 0,
3457
+ 0.25,
3458
+ 0.1,
3459
+ 1,
3460
+ 0,
3461
+ 0,
3462
+ 0,
3463
+ 0.25,
3464
+ -0.1,
3465
+ 1,
3466
+ 0,
3467
+ 0,
3468
+ 0,
3469
+ 0.25,
3470
+ -0.1,
3471
+ 1,
3472
+ 0,
3473
+ 0,
3474
+ 0,
3475
+ -0.45,
3476
+ -0.1,
3477
+ 1,
3478
+ 0,
3479
+ 0,
3480
+ 0,
3481
+ -0.45,
3482
+ -0.1,
3483
+ 1,
3484
+ 0,
3485
+ 0,
3486
+ 0,
3487
+ -0.45,
3488
+ -0.6,
3489
+ 1,
3490
+ 0,
3491
+ 0,
3492
+ 0,
3493
+ -0.25,
3494
+ -0.1,
3495
+ 1,
3496
+ 0,
3497
+ 0,
3498
+ 0,
3499
+ -0.25,
3500
+ -0.8,
3501
+ 1,
3502
+ 0,
3503
+ 0,
3504
+ 0,
3505
+ -0.25,
3506
+ -0.8,
3507
+ 1,
3508
+ 0,
3509
+ 0,
3510
+ 0,
3511
+ -0.25,
3512
+ -0.6,
3513
+ 1,
3514
+ 0,
3515
+ 0,
3516
+ 0,
3517
+ -0.25,
3518
+ -0.6,
3519
+ 1,
3520
+ 0,
3521
+ 0,
3522
+ 0,
3523
+ -0.25,
3524
+ -0.8,
3525
+ 1,
3526
+ 0,
3527
+ 0,
3528
+ 0,
3529
+ 0.45,
3530
+ -0.6,
3531
+ 1,
3532
+ 0,
3533
+ 0,
3534
+ 0,
3535
+ 0.25,
3536
+ -0.8,
3537
+ 1,
3538
+ 0,
3539
+ 0,
3540
+ 0,
3541
+ 0.25,
3542
+ -0.8,
3543
+ 1,
3544
+ 0,
3545
+ 0,
3546
+ 0,
3547
+ 0.25,
3548
+ -0.4,
3549
+ 1,
3550
+ 0,
3551
+ 0,
3552
+ 0,
3553
+ 0.25,
3554
+ -0.4,
3555
+ 1,
3556
+ 0,
3557
+ 0,
3558
+ 0,
3559
+ 0.25,
3560
+ -0.6,
3561
+ 1,
3562
+ 0,
3563
+ 0,
3564
+ 0,
3565
+ 0.45,
3566
+ -0.4,
3567
+ 1,
3568
+ 0,
3569
+ 0,
3570
+ 0,
3571
+ 0.45,
3572
+ -0.6,
3573
+ 1,
3574
+ 0,
3575
+ 0,
3576
+ 0,
3577
+ 0.45,
3578
+ -0.6,
3579
+ 1,
3580
+ 0,
3581
+ 0,
3582
+ 0,
3583
+ -0.1,
3584
+ -0.8,
3585
+ -1,
3586
+ 0,
3587
+ 0,
3588
+ 0,
3589
+ -0.1,
3590
+ -0.8,
3591
+ -1,
3592
+ 0,
3593
+ 0,
3594
+ 0,
3595
+ -0.1,
3596
+ 0.8,
3597
+ -1,
3598
+ 0,
3599
+ 0,
3600
+ 0,
3601
+ 0.1,
3602
+ -0.8,
3603
+ -1,
3604
+ 0,
3605
+ 0,
3606
+ 0,
3607
+ 0.1,
3608
+ 0.8,
3609
+ -1,
3610
+ 0,
3611
+ 0,
3612
+ 0,
3613
+ 0.1,
3614
+ 0.8,
3615
+ -1,
3616
+ 0,
3617
+ 0,
3618
+ 0,
3619
+ -1,
3620
+ -0.45,
3621
+ -0.8,
3622
+ 0,
3623
+ 0,
3624
+ 0,
3625
+ -1,
3626
+ -0.45,
3627
+ -0.8,
3628
+ 0,
3629
+ 0,
3630
+ 0,
3631
+ -1,
3632
+ -0.45,
3633
+ 0.8,
3634
+ 0,
3635
+ 0,
3636
+ 0,
3637
+ -1,
3638
+ -0.25,
3639
+ -0.8,
3640
+ 0,
3641
+ 0,
3642
+ 0,
3643
+ -1,
3644
+ -0.25,
3645
+ 0.8,
3646
+ 0,
3647
+ 0,
3648
+ 0,
3649
+ -1,
3650
+ -0.25,
3651
+ 0.8,
3652
+ 0,
3653
+ 0,
3654
+ 0,
3655
+ -1,
3656
+ -0.25,
3657
+ -0.8,
3658
+ 0,
3659
+ 0,
3660
+ 0,
3661
+ -1,
3662
+ -0.25,
3663
+ -0.8,
3664
+ 0,
3665
+ 0,
3666
+ 0,
3667
+ -1,
3668
+ -0.25,
3669
+ -0.6,
3670
+ 0,
3671
+ 0,
3672
+ 0,
3673
+ -1,
3674
+ 0.45,
3675
+ -0.8,
3676
+ 0,
3677
+ 0,
3678
+ 0,
3679
+ -1,
3680
+ 0.45,
3681
+ -0.6,
3682
+ 0,
3683
+ 0,
3684
+ 0,
3685
+ -1,
3686
+ 0.45,
3687
+ -0.6,
3688
+ 0,
3689
+ 0,
3690
+ 0,
3691
+ 1,
3692
+ 0.45,
3693
+ -0.8,
3694
+ 0,
3695
+ 0,
3696
+ 0,
3697
+ 1,
3698
+ 0.45,
3699
+ -0.8,
3700
+ 0,
3701
+ 0,
3702
+ 0,
3703
+ 1,
3704
+ 0.45,
3705
+ 0.8,
3706
+ 0,
3707
+ 0,
3708
+ 0,
3709
+ 1,
3710
+ 0.25,
3711
+ -0.8,
3712
+ 0,
3713
+ 0,
3714
+ 0,
3715
+ 1,
3716
+ 0.25,
3717
+ 0.8,
3718
+ 0,
3719
+ 0,
3720
+ 0,
3721
+ 1,
3722
+ 0.25,
3723
+ 0.8,
3724
+ 0,
3725
+ 0,
3726
+ 0,
3727
+ 1,
3728
+ 0.25,
3729
+ 0.6,
3730
+ 0,
3731
+ 0,
3732
+ 0,
3733
+ 1,
3734
+ 0.25,
3735
+ 0.6,
3736
+ 0,
3737
+ 0,
3738
+ 0,
3739
+ 1,
3740
+ 0.25,
3741
+ 0.8,
3742
+ 0,
3743
+ 0,
3744
+ 0,
3745
+ 1,
3746
+ -0.45,
3747
+ 0.6,
3748
+ 0,
3749
+ 0,
3750
+ 0,
3751
+ 1,
3752
+ -0.25,
3753
+ 0.8,
3754
+ 0,
3755
+ 0,
3756
+ 0,
3757
+ 1,
3758
+ -0.25,
3759
+ 0.8,
3760
+ 0,
3761
+ 0,
3762
+ 0,
3763
+ 1,
3764
+ -0.25,
3765
+ 0.1,
3766
+ 0,
3767
+ 0,
3768
+ 0,
3769
+ 1,
3770
+ -0.25,
3771
+ 0.1,
3772
+ 0,
3773
+ 0,
3774
+ 0,
3775
+ 1,
3776
+ -0.25,
3777
+ 0.6,
3778
+ 0,
3779
+ 0,
3780
+ 0,
3781
+ 1,
3782
+ -0.45,
3783
+ 0.1,
3784
+ 0,
3785
+ 0,
3786
+ 0,
3787
+ 1,
3788
+ -0.45,
3789
+ 0.6,
3790
+ 0,
3791
+ 0,
3792
+ 0,
3793
+ 1,
3794
+ -0.45,
3795
+ 0.6,
3796
+ 0,
3797
+ 0,
3798
+ 0,
3799
+ 1,
3800
+ 0.25,
3801
+ -0.1,
3802
+ 0,
3803
+ 0,
3804
+ 0,
3805
+ 1,
3806
+ 0.25,
3807
+ -0.1,
3808
+ 0,
3809
+ 0,
3810
+ 0,
3811
+ 1,
3812
+ 0.25,
3813
+ 0.1,
3814
+ 0,
3815
+ 0,
3816
+ 0,
3817
+ 1,
3818
+ -0.25,
3819
+ -0.1,
3820
+ 0,
3821
+ 0,
3822
+ 0,
3823
+ 1,
3824
+ -0.45,
3825
+ 0.1,
3826
+ 0,
3827
+ 0,
3828
+ 0,
3829
+ 1,
3830
+ -0.45,
3831
+ 0.1,
3832
+ 0,
3833
+ 0,
3834
+ 0,
3835
+ 1,
3836
+ -0.25,
3837
+ -0.8,
3838
+ 0,
3839
+ 0,
3840
+ 0,
3841
+ 1,
3842
+ -0.25,
3843
+ -0.8,
3844
+ 0,
3845
+ 0,
3846
+ 0,
3847
+ 1,
3848
+ -0.05,
3849
+ -0.1,
3850
+ 0,
3851
+ 0,
3852
+ 0,
3853
+ 1,
3854
+ -0.45,
3855
+ -0.8,
3856
+ 0,
3857
+ 0,
3858
+ 0,
3859
+ 1,
3860
+ -0.25,
3861
+ -0.1,
3862
+ 0,
3863
+ 0,
3864
+ 0,
3865
+ 1,
3866
+ -0.25,
3867
+ -0.1,
3868
+ 0,
3869
+ 0,
3870
+ 0
3871
+ ]);
2847
3872
  var NiivueObject3D = function(id, vertexBuffer, mode, indexCount, indexBuffer = null, vao = null) {
2848
3873
  this.BLEND = 1;
2849
3874
  this.CULL_FACE = 2;
@@ -103542,6 +104567,22 @@ function isPlatformLittleEndian() {
103542
104567
  new DataView(buffer2).setInt16(0, 256, true);
103543
104568
  return new Int16Array(buffer2)[0] === 256;
103544
104569
  }
104570
+ function NVImageFromUrlOptions(url, urlImageData = "", name = "", colorMap = "gray", opacity = 1, cal_min = NaN, cal_max = NaN, trustCalMinMax = true, percentileFrac = 0.02, ignoreZeroVoxels = false, visible = true, colorMapNegative = "") {
104571
+ return {
104572
+ url,
104573
+ urlImageData,
104574
+ name,
104575
+ colorMap,
104576
+ opacity,
104577
+ cal_min,
104578
+ cal_max,
104579
+ trustCalMinMax,
104580
+ percentileFrac,
104581
+ ignoreZeroVoxels,
104582
+ visible,
104583
+ colorMapNegative
104584
+ };
104585
+ }
103545
104586
  function NVImage(dataBuffer, name = "", colorMap = "gray", opacity = 1, pairedImgData = null, cal_min = NaN, cal_max = NaN, trustCalMinMax = true, percentileFrac = 0.02, ignoreZeroVoxels = false, visible = true, isDICOMDIR = false, useQFormNotSForm = false, colorMapNegative = "") {
103546
104587
  this.DT_NONE = 0;
103547
104588
  this.DT_UNKNOWN = 0;
@@ -103868,9 +104909,9 @@ NVImage.prototype.calculateOblique = function() {
103868
104909
  let XY = Math.abs(90 - angle(X1mm, Y1mm) * (180 / Math.PI));
103869
104910
  let XZ = Math.abs(90 - angle(X1mm, Z1mm) * (180 / Math.PI));
103870
104911
  let YZ = Math.abs(90 - angle(Y1mm, Z1mm) * (180 / Math.PI));
103871
- let maxShear = Math.max(Math.max(XY, XZ), YZ);
103872
- if (maxShear > 0.1)
103873
- log$2.debug("Warning: shear detected (gantry tilt) of %f degrees", maxShear);
104912
+ this.maxShearDeg = Math.max(Math.max(XY, XZ), YZ);
104913
+ if (this.maxShearDeg > 0.1)
104914
+ console.log("Warning: voxels are rhomboidal, maximum shear is %f degrees.", this.maxShearDeg);
103874
104915
  let dim = fromValues(this.dimsRAS[1], this.dimsRAS[2], this.dimsRAS[3], 1);
103875
104916
  let sform = clone$1(this.matRAS);
103876
104917
  transpose(sform, sform);
@@ -103986,7 +105027,8 @@ NVImage.prototype.readDICOM = function(buf) {
103986
105027
  this.series = new daikon.Series();
103987
105028
  if (Array.isArray(buf)) {
103988
105029
  for (let i2 = 0; i2 < buf.length; i2++) {
103989
- let image3 = daikon.Series.parseImage(new DataView(buf[i2]));
105030
+ const dataview = new DataView(buf[i2]);
105031
+ let image3 = daikon.Series.parseImage(dataview);
103990
105032
  if (image3 === null) {
103991
105033
  console.error(daikon.Series.parserError);
103992
105034
  } else if (image3.hasPixelData()) {
@@ -104064,8 +105106,6 @@ NVImage.prototype.readDICOM = function(buf) {
104064
105106
  [0, 0, 0, 1]
104065
105107
  ];
104066
105108
  }
104067
- console.log("DICOM", this.series.images[0]);
104068
- console.log("NIfTI", hdr);
104069
105109
  let imgRaw = [];
104070
105110
  let data;
104071
105111
  let length2 = this.series.validatePixelDataLength(this.series.images[0]);
@@ -104215,30 +105255,6 @@ NVImage.prototype.readVMR = function(buffer2) {
104215
105255
  let nBytes = hdr.dims[1] * hdr.dims[2] * hdr.dims[3];
104216
105256
  if (version >= 4) {
104217
105257
  let pos = 8 + nBytes;
104218
- reader.getUint16(pos, true);
104219
- reader.getUint16(pos + 2, true);
104220
- reader.getUint16(pos + 4, true);
104221
- reader.getUint16(pos + 6, true);
104222
- reader.getUint32(pos + 8, true);
104223
- reader.getUint32(pos + 12, true);
104224
- reader.getFloat32(pos + 16, true);
104225
- reader.getFloat32(pos + 20, true);
104226
- reader.getFloat32(pos + 24, true);
104227
- reader.getFloat32(pos + 28, true);
104228
- reader.getFloat32(pos + 32, true);
104229
- reader.getFloat32(pos + 36, true);
104230
- reader.getFloat32(pos + 40, true);
104231
- reader.getFloat32(pos + 44, true);
104232
- reader.getFloat32(pos + 48, true);
104233
- reader.getFloat32(pos + 52, true);
104234
- reader.getFloat32(pos + 56, true);
104235
- reader.getFloat32(pos + 60, true);
104236
- reader.getUint32(pos + 64, true);
104237
- reader.getUint32(pos + 68, true);
104238
- reader.getFloat32(pos + 72, true);
104239
- reader.getFloat32(pos + 76, true);
104240
- reader.getFloat32(pos + 80, true);
104241
- reader.getFloat32(pos + 84, true);
104242
105258
  let nSpatialTransforms = reader.getUint32(pos + 88, true);
104243
105259
  pos = pos + 92;
104244
105260
  if (nSpatialTransforms > 0) {
@@ -104247,7 +105263,6 @@ NVImage.prototype.readVMR = function(buffer2) {
104247
105263
  while (pos < len2 && reader.getUint8(pos) !== 0)
104248
105264
  pos++;
104249
105265
  pos++;
104250
- reader.getUint32(pos, true);
104251
105266
  pos += 4;
104252
105267
  while (pos < len2 && reader.getUint8(pos) !== 0)
104253
105268
  pos++;
@@ -104258,16 +105273,9 @@ NVImage.prototype.readVMR = function(buffer2) {
104258
105273
  pos += 4;
104259
105274
  }
104260
105275
  }
104261
- reader.getUint8(pos);
104262
- reader.getUint8(pos + 1);
104263
105276
  hdr.pixDims[1] = reader.getFloat32(pos + 2, true);
104264
105277
  hdr.pixDims[2] = reader.getFloat32(pos + 6, true);
104265
105278
  hdr.pixDims[3] = reader.getFloat32(pos + 10, true);
104266
- reader.getUint8(pos + 14);
104267
- reader.getUint8(pos + 15);
104268
- reader.getInt32(pos + 16, true);
104269
- reader.getInt32(pos + 20, true);
104270
- reader.getInt32(pos + 24, true);
104271
105279
  }
104272
105280
  console.log("Warning: VMR spatial transform not implemented");
104273
105281
  hdr.affine = [
@@ -104299,8 +105307,6 @@ NVImage.prototype.readMGH = function(buffer2) {
104299
105307
  let depth = reader.getInt32(12, false);
104300
105308
  let nframes = reader.getInt32(16, false);
104301
105309
  let mtype = reader.getInt32(20, false);
104302
- reader.getInt32(24, false);
104303
- reader.getInt16(28, false);
104304
105310
  let spacingX = reader.getFloat32(30, false);
104305
105311
  let spacingY = reader.getFloat32(34, false);
104306
105312
  let spacingZ = reader.getFloat32(38, false);
@@ -104313,9 +105319,6 @@ NVImage.prototype.readMGH = function(buffer2) {
104313
105319
  let zr = reader.getFloat32(66, false);
104314
105320
  let za = reader.getFloat32(70, false);
104315
105321
  let zs = reader.getFloat32(74, false);
104316
- reader.getFloat32(78, false);
104317
- reader.getFloat32(82, false);
104318
- reader.getFloat32(86, false);
104319
105322
  if (version !== 1 || mtype < 0 || mtype > 4)
104320
105323
  console.log("Not a valid MGH file");
104321
105324
  if (mtype === 0) {
@@ -105001,7 +106004,6 @@ NVImage.prototype.readNRRD = function(dataBuffer, pairedImgData) {
105001
106004
  [0, 0, 0, 1]
105002
106005
  ];
105003
106006
  }
105004
- hdr.dims[1] * hdr.dims[2] * hdr.dims[3];
105005
106007
  if (isDetached && pairedImgData) {
105006
106008
  return pairedImgData.slice();
105007
106009
  }
@@ -105477,14 +106479,15 @@ NVImage.loadFromFile = async function({
105477
106479
  }
105478
106480
  } else {
105479
106481
  dataBuffer = await this.readFileAsync(file);
106482
+ name = file.name;
105480
106483
  }
105481
106484
  let pairedImgData = null;
105482
106485
  if (urlImgData) {
105483
106486
  pairedImgData = await this.readFileAsync(urlImgData);
105484
106487
  }
105485
- name = file.name;
105486
106488
  nvimage = new NVImage(dataBuffer, name, colorMap, opacity, pairedImgData, cal_min, cal_max, trustCalMinMax, percentileFrac, ignoreZeroVoxels, visible, isDICOMDIR);
105487
106489
  } catch (err2) {
106490
+ console.log(err2);
105488
106491
  log$2.debug(err2);
105489
106492
  }
105490
106493
  return nvimage;
@@ -105742,6 +106745,24 @@ NVImage.prototype.toNiivueObject3D = function(id, gl) {
105742
106745
  ];
105743
106746
  return obj3D;
105744
106747
  };
106748
+ NVImage.prototype.applyOptionsUpdate = function(options) {
106749
+ this.hdr.cal_min = options.cal_min;
106750
+ this.hdr.cal_max = options.cal_max;
106751
+ delete options["url"];
106752
+ delete options["urlImageData"];
106753
+ delete options["cal_min"];
106754
+ delete options["cal_max"];
106755
+ Object.assign(this, options);
106756
+ };
106757
+ NVImage.prototype.getImageOptions = function() {
106758
+ let options = null;
106759
+ try {
106760
+ options = new NVImageFromUrlOptions("", "", this.name, this.colorMap, this.opacity, this.hdr.cal_min, this.hdr.cal_max, this.trustCalMinMax, this.percentileFrac, this.ignoreZeroVoxels, this.visible, this.colorMapNegative);
106761
+ } catch (e) {
106762
+ console.log(e);
106763
+ }
106764
+ return options;
106765
+ };
105745
106766
  const colortables = function() {
105746
106767
  this.version = 0.1;
105747
106768
  this.gamma = 1;
@@ -105765,7 +106786,7 @@ colortables.prototype.colormap = function(lutName = "") {
105765
106786
  return this.makeLut(cmaps[defaultLutName].R, cmaps[defaultLutName].G, cmaps[defaultLutName].B, cmaps[defaultLutName].A, cmaps[defaultLutName].I);
105766
106787
  };
105767
106788
  colortables.prototype.colormapFromKey = function(name) {
105768
- let availMaps = luts.colorMaps();
106789
+ let availMaps = this.colorMaps();
105769
106790
  for (let i2 = 0; i2 < availMaps.length; i2++) {
105770
106791
  let key2 = availMaps[i2];
105771
106792
  if (name.toLowerCase() === key2.toLowerCase()) {
@@ -105799,7 +106820,18 @@ colortables.prototype.makeLut = function(Rs, Gs, Bs, As, Is) {
105799
106820
  };
105800
106821
  const cmapper$1 = new colortables();
105801
106822
  const log$1 = new Log();
105802
- function NVMesh(pts, tris, name = "", rgba255 = [1, 0, 0, 0], opacity = 1, visible = true, gl, connectome = null, dpg = null, dps = null, dpv = null) {
106823
+ function NVMeshFromUrlOptions(url = "", gl = null, name = "", opacity = 1, rgba255 = [255, 255, 255, 255], visible = true, layers = []) {
106824
+ return {
106825
+ url,
106826
+ gl,
106827
+ name,
106828
+ opacity,
106829
+ rgba255,
106830
+ visible,
106831
+ layers
106832
+ };
106833
+ }
106834
+ function NVMesh(pts, tris, name = "", rgba255 = [255, 255, 255, 255], opacity = 1, visible = true, gl, connectome = null, dpg = null, dps = null, dpv = null) {
105803
106835
  this.name = name;
105804
106836
  this.id = v4();
105805
106837
  let obj = getExtents(pts);
@@ -105808,6 +106840,7 @@ function NVMesh(pts, tris, name = "", rgba255 = [1, 0, 0, 0], opacity = 1, visib
105808
106840
  this.extentsMax = obj.extentsMax;
105809
106841
  this.opacity = opacity > 1 ? 1 : opacity;
105810
106842
  this.visible = visible;
106843
+ this.meshShaderIndex = 0;
105811
106844
  this.indexBuffer = gl.createBuffer();
105812
106845
  this.vertexBuffer = gl.createBuffer();
105813
106846
  this.vao = gl.createVertexArray();
@@ -106085,6 +107118,45 @@ NVMesh.prototype.updateConnectome = function(gl) {
106085
107118
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(posNormClr), gl.STATIC_DRAW);
106086
107119
  this.indexCount = tris.length;
106087
107120
  };
107121
+ function getClusterBoundaryU8(u82, faces) {
107122
+ const border = new Array(u82.length).fill(false);
107123
+ const binary = new Array(u82.length).fill(false);
107124
+ for (let i2 = 0; i2 < u82.length; i2++)
107125
+ if (u82[i2] > 0)
107126
+ binary[i2] = true;
107127
+ let nTri = faces.length / 3;
107128
+ let j = 0;
107129
+ for (let i2 = 0; i2 < nTri; i2++) {
107130
+ let v0 = faces[j];
107131
+ let v1 = faces[j + 1];
107132
+ let v2 = faces[j + 2];
107133
+ j += 3;
107134
+ if (binary[v0] === binary[v1] && binary[v0] === binary[v2] && binary[v1] === binary[v2])
107135
+ continue;
107136
+ border[v0] = true;
107137
+ border[v1] = true;
107138
+ border[v2] = true;
107139
+ }
107140
+ return border;
107141
+ }
107142
+ function getClusterBoundary(rgba8, faces) {
107143
+ var rgba32 = new Uint32Array(rgba8.buffer);
107144
+ const border = new Array(rgba32.length).fill(false);
107145
+ let nTri = faces.length / 3;
107146
+ let j = 0;
107147
+ for (let i2 = 0; i2 < nTri; i2++) {
107148
+ let v0 = faces[j];
107149
+ let v1 = faces[j + 1];
107150
+ let v2 = faces[j + 2];
107151
+ j += 3;
107152
+ if (rgba32[v0] === rgba32[v1] && rgba32[v0] === rgba32[v2] && rgba32[v1] === rgba32[v2])
107153
+ continue;
107154
+ border[v0] = true;
107155
+ border[v1] = true;
107156
+ border[v2] = true;
107157
+ }
107158
+ return border;
107159
+ }
106088
107160
  NVMesh.prototype.updateMesh = function(gl) {
106089
107161
  if (this.offsetPt0) {
106090
107162
  this.updateFibers(gl);
@@ -106104,16 +107176,25 @@ NVMesh.prototype.updateMesh = function(gl) {
106104
107176
  let lerp2 = function(x2, y, a) {
106105
107177
  return x2 * (1 - a) + y * a;
106106
107178
  };
107179
+ let nvtx = this.pts.length / 3;
106107
107180
  let layer = this.layers[i2];
106108
- if (layer.opacity <= 0 || layer.cal_min >= layer.cal_max)
107181
+ if (layer.opacity <= 0 || layer.cal_min > layer.cal_max)
106109
107182
  continue;
106110
107183
  let opacity = layer.opacity;
106111
107184
  var u82 = new Uint8Array(posNormClr.buffer);
106112
107185
  if (layer.values.constructor === Uint32Array) {
106113
107186
  let rgba8 = new Uint8Array(layer.values.buffer);
107187
+ let opaque = new Array(nvtx).fill(true);
107188
+ if (layer.isOutlineBorder)
107189
+ opaque = getClusterBoundary(rgba8, this.tris);
106114
107190
  let k = 0;
106115
107191
  for (let j = 0; j < layer.values.length; j++) {
106116
107192
  let vtx = j * 28 + 24;
107193
+ if (!opaque[j]) {
107194
+ u82[vtx + 3] = 0;
107195
+ k += 4;
107196
+ continue;
107197
+ }
106117
107198
  u82[vtx + 0] = lerp2(u82[vtx + 0], rgba8[k + 0], opacity);
106118
107199
  u82[vtx + 1] = lerp2(u82[vtx + 1], rgba8[k + 1], opacity);
106119
107200
  u82[vtx + 2] = lerp2(u82[vtx + 2], rgba8[k + 2], opacity);
@@ -106123,34 +107204,76 @@ NVMesh.prototype.updateMesh = function(gl) {
106123
107204
  }
106124
107205
  let lut = cmapper$1.colormap(layer.colorMap);
106125
107206
  let frame = Math.min(Math.max(layer.frame4D, 0), layer.nFrame4D - 1);
106126
- let nvtx = this.pts.length / 3;
106127
107207
  let frameOffset = nvtx * frame;
106128
107208
  if (layer.useNegativeCmap) {
106129
107209
  layer.cal_min = Math.max(0, layer.cal_min);
106130
107210
  layer.cal_max = Math.max(layer.cal_min + 1e-6, layer.cal_max);
106131
107211
  }
106132
107212
  let scale255 = 255 / (layer.cal_max - layer.cal_min);
106133
- for (let j = 0; j < nvtx; j++) {
106134
- let v255 = Math.round((layer.values[j + frameOffset] - layer.cal_min) * scale255);
106135
- if (v255 < 0)
106136
- continue;
106137
- v255 = Math.min(255, v255) * 4;
106138
- let vtx = j * 28 + 24;
106139
- u82[vtx + 0] = lerp2(u82[vtx + 0], lut[v255 + 0], opacity);
106140
- u82[vtx + 1] = lerp2(u82[vtx + 1], lut[v255 + 1], opacity);
106141
- u82[vtx + 2] = lerp2(u82[vtx + 2], lut[v255 + 2], opacity);
106142
- }
106143
- if (layer.useNegativeCmap) {
106144
- let lut2 = cmapper$1.colormap(layer.colorMapNegative);
107213
+ if (!layer.isOutlineBorder) {
106145
107214
  for (let j = 0; j < nvtx; j++) {
106146
- let v255 = Math.round((-layer.values[j + frameOffset] - layer.cal_min) * scale255);
107215
+ let v255 = Math.round((layer.values[j + frameOffset] - layer.cal_min) * scale255);
106147
107216
  if (v255 < 0)
106148
107217
  continue;
106149
107218
  v255 = Math.min(255, v255) * 4;
106150
107219
  let vtx = j * 28 + 24;
106151
- u82[vtx + 0] = lerp2(u82[vtx + 0], lut2[v255 + 0], opacity);
106152
- u82[vtx + 1] = lerp2(u82[vtx + 1], lut2[v255 + 1], opacity);
106153
- u82[vtx + 2] = lerp2(u82[vtx + 2], lut2[v255 + 2], opacity);
107220
+ u82[vtx + 0] = lerp2(u82[vtx + 0], lut[v255 + 0], opacity);
107221
+ u82[vtx + 1] = lerp2(u82[vtx + 1], lut[v255 + 1], opacity);
107222
+ u82[vtx + 2] = lerp2(u82[vtx + 2], lut[v255 + 2], opacity);
107223
+ }
107224
+ } else {
107225
+ let v255s = new Uint8Array(nvtx);
107226
+ for (let j = 0; j < nvtx; j++) {
107227
+ let v255 = Math.round((layer.values[j + frameOffset] - layer.cal_min) * scale255);
107228
+ if (v255 < 0)
107229
+ continue;
107230
+ v255 = Math.min(255, v255);
107231
+ v255s[j] = v255;
107232
+ }
107233
+ let opaque = getClusterBoundaryU8(v255s, this.tris);
107234
+ for (let j = 0; j < nvtx; j++) {
107235
+ let v255 = 255;
107236
+ if (!opaque[j])
107237
+ continue;
107238
+ v255 = Math.min(255, v255) * 4;
107239
+ let vtx = j * 28 + 24;
107240
+ u82[vtx + 0] = lerp2(u82[vtx + 0], lut[v255 + 0], opacity);
107241
+ u82[vtx + 1] = lerp2(u82[vtx + 1], lut[v255 + 1], opacity);
107242
+ u82[vtx + 2] = lerp2(u82[vtx + 2], lut[v255 + 2], opacity);
107243
+ }
107244
+ }
107245
+ if (layer.useNegativeCmap) {
107246
+ let lut2 = cmapper$1.colormap(layer.colorMapNegative);
107247
+ if (!layer.isOutlineBorder) {
107248
+ for (let j = 0; j < nvtx; j++) {
107249
+ let v255 = Math.round((-layer.values[j + frameOffset] - layer.cal_min) * scale255);
107250
+ if (v255 < 0)
107251
+ continue;
107252
+ v255 = Math.min(255, v255) * 4;
107253
+ let vtx = j * 28 + 24;
107254
+ u82[vtx + 0] = lerp2(u82[vtx + 0], lut2[v255 + 0], opacity);
107255
+ u82[vtx + 1] = lerp2(u82[vtx + 1], lut2[v255 + 1], opacity);
107256
+ u82[vtx + 2] = lerp2(u82[vtx + 2], lut2[v255 + 2], opacity);
107257
+ }
107258
+ } else {
107259
+ let v255s = new Uint8Array(nvtx);
107260
+ for (let j = 0; j < nvtx; j++) {
107261
+ let v255 = Math.round((-layer.values[j + frameOffset] - layer.cal_min) * scale255);
107262
+ if (v255 < 0)
107263
+ continue;
107264
+ v255s[j] = Math.min(255, v255);
107265
+ }
107266
+ let opaque = getClusterBoundaryU8(v255s, this.tris);
107267
+ for (let j = 0; j < nvtx; j++) {
107268
+ let v255 = 255;
107269
+ if (!opaque[j])
107270
+ continue;
107271
+ v255 = Math.min(255, v255) * 4;
107272
+ let vtx = j * 28 + 24;
107273
+ u82[vtx + 0] = lerp2(u82[vtx + 0], lut2[v255 + 0], opacity);
107274
+ u82[vtx + 1] = lerp2(u82[vtx + 1], lut2[v255 + 1], opacity);
107275
+ u82[vtx + 2] = lerp2(u82[vtx + 2], lut2[v255 + 2], opacity);
107276
+ }
106154
107277
  }
106155
107278
  }
106156
107279
  }
@@ -106336,7 +107459,6 @@ NVMesh.readTRACT = function(buffer2) {
106336
107459
  let bundleTag = readNumericTag("Bundle_Tag=");
106337
107460
  let isLittleEndian2 = line.includes("binary.lsbfirst");
106338
107461
  for (let i2 = 0; i2 < new_tracts; i2++) {
106339
- reader.getUint32(pos, isLittleEndian2);
106340
107462
  pos += 4;
106341
107463
  let new_pts = reader.getUint32(pos, isLittleEndian2) / 3;
106342
107464
  pos += 4;
@@ -106768,10 +107890,7 @@ NVMesh.readSMP = function(buffer2, n_vert) {
106768
107890
  return scalars;
106769
107891
  };
106770
107892
  NVMesh.readSTC = function(buffer2, n_vert) {
106771
- buffer2.byteLength;
106772
107893
  var reader = new DataView(buffer2);
106773
- reader.getFloat32(0, false);
106774
- reader.getFloat32(4, false);
106775
107894
  let n_vertex = reader.getInt32(8, false);
106776
107895
  if (n_vertex !== n_vert) {
106777
107896
  console.log("Overlay has " + n_vertex + " vertices, expected " + n_vert);
@@ -106793,7 +107912,6 @@ NVMesh.readCURV = function(buffer2, n_vert) {
106793
107912
  let sig1 = view.getUint8(1);
106794
107913
  let sig2 = view.getUint8(2);
106795
107914
  let n_vertex = view.getUint32(3, false);
106796
- view.getUint32(7, false);
106797
107915
  let n_time = view.getUint32(11, false);
106798
107916
  if (sig0 !== 255 || sig1 !== 255 || sig2 !== 255)
106799
107917
  log$1.debug("Unable to recognize file type: does not appear to be FreeSurfer format.");
@@ -107385,7 +108503,7 @@ NVMesh.readPLY = function(buffer2) {
107385
108503
  indices
107386
108504
  };
107387
108505
  };
107388
- NVMesh.readLayer = function(name, buffer2, nvmesh, opacity = 0.5, colorMap = "warm", colorMapNegative = "winter", useNegativeCmap = false, cal_min = null, cal_max = null) {
108506
+ NVMesh.readLayer = function(name, buffer2, nvmesh, opacity = 0.5, colorMap = "warm", colorMapNegative = "winter", useNegativeCmap = false, cal_min = null, cal_max = null, isOutlineBorder = false) {
107389
108507
  let layer = [];
107390
108508
  let n_vert = nvmesh.vertexCount / 3;
107391
108509
  if (n_vert < 3)
@@ -107421,6 +108539,7 @@ NVMesh.readLayer = function(name, buffer2, nvmesh, opacity = 0.5, colorMap = "wa
107421
108539
  return;
107422
108540
  layer.nFrame4D = layer.values.length / n_vert;
107423
108541
  layer.frame4D = 0;
108542
+ layer.isOutlineBorder = isOutlineBorder;
107424
108543
  let mn = layer.values[0];
107425
108544
  let mx = layer.values[0];
107426
108545
  for (var i2 = 0; i2 < layer.values.length; i2++) {
@@ -107445,7 +108564,6 @@ NVMesh.readOFF = function(buffer2) {
107445
108564
  var enc = new TextDecoder("utf-8");
107446
108565
  var txt = enc.decode(buffer2);
107447
108566
  var lines = txt.split("\n");
107448
- lines.length;
107449
108567
  let pts = [];
107450
108568
  let t = [];
107451
108569
  let i2 = 0;
@@ -107688,8 +108806,10 @@ NVMesh.readNII2 = function(buffer2, n_vert = 0) {
107688
108806
  return scalars;
107689
108807
  }
107690
108808
  let voxoffset2 = Number(reader.getBigInt64(168, isLittleEndian2));
107691
- reader.getFloat64(176, isLittleEndian2);
107692
- reader.getFloat64(184, isLittleEndian2);
108809
+ let scl_slope = reader.getFloat64(176, isLittleEndian2);
108810
+ let scl_inter = reader.getFloat64(184, isLittleEndian2);
108811
+ if (scl_slope !== 1 || scl_inter !== 0)
108812
+ console.log("ignoring scale slope and intercept");
107693
108813
  let intent_code = reader.getUint32(504, isLittleEndian2);
107694
108814
  let datatype = reader.getUint16(12, isLittleEndian2);
107695
108815
  if (datatype !== 2 && datatype !== 4 && datatype !== 8 && datatype !== 16) {
@@ -107839,7 +108959,6 @@ NVMesh.readNII2 = function(buffer2, n_vert = 0) {
107839
108959
  };
107840
108960
  NVMesh.readNII = function(buffer2, n_vert = 0) {
107841
108961
  let scalars = [];
107842
- buffer2.byteLength;
107843
108962
  let isLittleEndian2 = true;
107844
108963
  var reader = new DataView(buffer2);
107845
108964
  var magic = reader.getUint16(0, isLittleEndian2);
@@ -107864,8 +108983,10 @@ NVMesh.readNII = function(buffer2, n_vert = 0) {
107864
108983
  if (magic !== 348)
107865
108984
  console.log("Not a valid NIfTI image.");
107866
108985
  let voxoffset2 = reader.getFloat32(108, isLittleEndian2);
107867
- reader.getFloat32(112, isLittleEndian2);
107868
- reader.getFloat32(116, isLittleEndian2);
108986
+ let scl_slope = reader.getFloat32(112, isLittleEndian2);
108987
+ let scl_inter = reader.getFloat32(116, isLittleEndian2);
108988
+ if (scl_slope !== 1 || scl_inter !== 0)
108989
+ console.log("ignoring scale slope and intercept");
107869
108990
  let datatype = reader.getUint16(70, isLittleEndian2);
107870
108991
  if (datatype !== 2 && datatype !== 4 && datatype !== 8 && datatype !== 16) {
107871
108992
  console.log("Unsupported NIfTI datatype " + datatype);
@@ -108149,7 +109270,6 @@ NVMesh.readX3D = function(buffer2, n_vert = 0) {
108149
109270
  rotation[1],
108150
109271
  rotation[2]
108151
109272
  ]);
108152
- fromValues(translation[0], translation[1], translation[2], 1);
108153
109273
  let pti = fromValues(0, -height * 0.5, 0, 1);
108154
109274
  let ptj = fromValues(0, +height * 0.5, 0, 1);
108155
109275
  transformMat4(pti, pti, r);
@@ -110409,8 +111529,82 @@ var WebSocketSubject = function(_super) {
110409
111529
  function webSocket(urlConfigOrSource) {
110410
111530
  return new WebSocketSubject(urlConfigOrSource);
110411
111531
  }
111532
+ const UPDATE = "update";
111533
+ const CREATE = "create";
111534
+ const JOIN = "join";
111535
+ const ADD_VOLUME_URL = "add volume url";
111536
+ const REMOVE_VOLUME_URL = "remove volume media";
111537
+ const ADD_MESH_URL = "add mesh url";
111538
+ const REMOVE_MESH_URL = "remove mesh media";
111539
+ const SET_4D_VOL_INDEX = "set 4d vol index";
111540
+ const UPDATE_IMAGE_OPTIONS = "update image options";
111541
+ function NVMesssageUpdateData(azimuth, elevation, clipPlane, zoom) {
111542
+ return {
111543
+ azimuth,
111544
+ elevation,
111545
+ clipPlane,
111546
+ zoom
111547
+ };
111548
+ }
111549
+ function NVMessageSet4DVolumeIndexData(url, index) {
111550
+ return {
111551
+ url,
111552
+ index
111553
+ };
111554
+ }
111555
+ function NVMessage(messageType, messageData = "", sessionKey = "") {
111556
+ let message = {};
111557
+ message.key = sessionKey;
111558
+ message.op = messageType;
111559
+ switch (messageType) {
111560
+ case UPDATE:
111561
+ Object.assign(message, messageData);
111562
+ break;
111563
+ case UPDATE_IMAGE_OPTIONS:
111564
+ case ADD_VOLUME_URL:
111565
+ message.urlImageOptions = messageData;
111566
+ break;
111567
+ case ADD_MESH_URL:
111568
+ message.urlMeshOptions = messageData;
111569
+ break;
111570
+ case REMOVE_VOLUME_URL:
111571
+ case REMOVE_MESH_URL:
111572
+ message.url = messageData;
111573
+ break;
111574
+ case SET_4D_VOL_INDEX:
111575
+ message.url = messageData.url;
111576
+ message.index = messageData.index;
111577
+ break;
111578
+ }
111579
+ return message;
111580
+ }
110412
111581
  const log = new Log();
110413
111582
  const cmapper = new colortables();
111583
+ const MESH_EXTENSIONS = [
111584
+ "ASC",
111585
+ "DFS",
111586
+ "FSM",
111587
+ "PIAL",
111588
+ "ORIG",
111589
+ "INFLATED",
111590
+ "SMOOTHWM",
111591
+ "SPHERE",
111592
+ "WHITE",
111593
+ "GII",
111594
+ "MZ3",
111595
+ "NV",
111596
+ "OBJ",
111597
+ "OFF",
111598
+ "PLY",
111599
+ "SRF",
111600
+ "STL",
111601
+ "TCK",
111602
+ "TRACT",
111603
+ "TRK",
111604
+ "TRX",
111605
+ "VTK",
111606
+ "X3D"
111607
+ ];
110414
111608
  function Niivue(options = {}) {
110415
111609
  this.opts = {};
110416
111610
  this.defaults = {
@@ -110428,17 +111622,24 @@ function Niivue(options = {}) {
110428
111622
  trustCalMinMax: true,
110429
111623
  clipPlaneHotKey: "KeyC",
110430
111624
  viewModeHotKey: "KeyV",
111625
+ doubleTouchTimeout: 500,
111626
+ longTouchTimeout: 1e3,
110431
111627
  keyDebounceTime: 50,
110432
111628
  isNearestInterpolation: false,
110433
111629
  isAtlasOutline: false,
110434
111630
  isRuler: false,
110435
111631
  isColorbar: false,
111632
+ isOrientCube: false,
111633
+ multiplanarPadPixels: 0,
110436
111634
  isRadiologicalConvention: false,
110437
111635
  meshThicknessOn2D: Infinity,
110438
111636
  isDragShowsMeasurementTool: false,
111637
+ isDragForPanZoom: false,
111638
+ isDepthPickMesh: false,
110439
111639
  isCornerOrientationText: false,
110440
111640
  sagittalNoseLeft: false,
110441
111641
  isSliceMM: false,
111642
+ isHighResolutionCapable: true,
110442
111643
  logging: false,
110443
111644
  loadingText: "waiting for images...",
110444
111645
  dragAndDropEnabled: true,
@@ -110446,11 +111647,20 @@ function Niivue(options = {}) {
110446
111647
  penValue: 1,
110447
111648
  isFilledPen: false,
110448
111649
  thumbnail: "",
111650
+ maxDrawUndoBitmaps: 8,
110449
111651
  onLocationChange: () => {
110450
111652
  },
110451
111653
  onIntensityChange: () => {
110452
111654
  },
110453
111655
  onImageLoaded: () => {
111656
+ },
111657
+ onError: () => {
111658
+ },
111659
+ onInfo: () => {
111660
+ },
111661
+ onWarn: () => {
111662
+ },
111663
+ onDebug: () => {
110454
111664
  }
110455
111665
  };
110456
111666
  this.canvas = null;
@@ -110459,8 +111669,6 @@ function Niivue(options = {}) {
110459
111669
  this.volumeTexture = null;
110460
111670
  this.drawTexture = null;
110461
111671
  this.drawBitmap = null;
110462
- this.maxDrawUndoBitmaps = 8;
110463
- this.currentDrawUndoBitmap = this.maxDrawUndoBitmaps;
110464
111672
  this.drawUndoBitmaps = [];
110465
111673
  this.drawOpacity = 0.8;
110466
111674
  this.colorbarHeight = 0;
@@ -110472,10 +111680,12 @@ function Niivue(options = {}) {
110472
111680
  this.overlayTextureID = [];
110473
111681
  this.sliceShader = null;
110474
111682
  this.sliceMMShader = null;
111683
+ this.orientCubeShader = null;
111684
+ this.orientCubeShaderVAO = null;
110475
111685
  this.rectShader = null;
110476
- this.graphShader = null;
110477
111686
  this.renderShader = null;
110478
- this.pickingShader = null;
111687
+ this.pickingMeshShader = null;
111688
+ this.pickingImageShader = null;
110479
111689
  this.colorbarShader = null;
110480
111690
  this.fontShader = null;
110481
111691
  this.fontTexture = null;
@@ -110491,11 +111701,9 @@ function Niivue(options = {}) {
110491
111701
  this.orientShaderF = null;
110492
111702
  this.orientShaderRGBU = null;
110493
111703
  this.surfaceShader = null;
110494
- this.meshShader = null;
110495
111704
  this.genericVAO = null;
110496
111705
  this.unusedVAO = null;
110497
111706
  this.crosshairs3D = null;
110498
- this.pickingSurfaceShader = null;
110499
111707
  this.DEFAULT_FONT_GLYPH_SHEET = defaultFontPNG;
110500
111708
  this.DEFAULT_FONT_METRICS = defaultFontMetrics;
110501
111709
  this.fontMets = null;
@@ -110523,11 +111731,15 @@ function Niivue(options = {}) {
110523
111731
  this.scene.mouseButtonRightDown = false;
110524
111732
  this.scene.mouseDepthPicker = false;
110525
111733
  this.scene.pan2Dxyzmm = [0, 0, 0, 1];
110526
- this.scene.scale2D = 1;
111734
+ this.scene.pan2DxyzmmAtMouseDown = [0, 0, 0, 1];
110527
111735
  this.scene.prevX = 0;
110528
111736
  this.scene.prevY = 0;
110529
111737
  this.scene.currX = 0;
110530
111738
  this.scene.currY = 0;
111739
+ this.currentTouchTime = 0;
111740
+ this.lastTouchTime = 0;
111741
+ this.touchTimer = null;
111742
+ this.doubleTouch = false;
110531
111743
  this.back = {};
110532
111744
  this.overlays = [];
110533
111745
  this.volumes = [];
@@ -110588,10 +111800,6 @@ function Niivue(options = {}) {
110588
111800
  {
110589
111801
  Name: "Toon",
110590
111802
  Frag: fragMeshToonShader
110591
- },
110592
- {
110593
- Name: "Depth",
110594
- Frag: fragMeshDepthShader
110595
111803
  }
110596
111804
  ];
110597
111805
  this.isController = false;
@@ -110600,10 +111808,12 @@ function Niivue(options = {}) {
110600
111808
  this.sessionUrl = "";
110601
111809
  this.serverConnection$ = null;
110602
111810
  this.interval$ = null;
111811
+ this.mediaUrlMap = /* @__PURE__ */ new Map();
110603
111812
  this.initialized = false;
110604
111813
  for (let prop in this.defaults) {
110605
111814
  this.opts[prop] = options[prop] === void 0 ? this.defaults[prop] : options[prop];
110606
111815
  }
111816
+ this.currentDrawUndoBitmap = this.opts.maxDrawUndoBitmaps;
110607
111817
  if (this.opts.drawingEnabled) {
110608
111818
  this.createEmptyDrawing();
110609
111819
  }
@@ -110616,6 +111826,11 @@ function Niivue(options = {}) {
110616
111826
  loading: this.scene.loading$
110617
111827
  };
110618
111828
  this.subscriptions = [];
111829
+ if (this.opts.isHighResolutionCapable) {
111830
+ this.scene.dpr = window.devicePixelRatio || 1;
111831
+ } else {
111832
+ this.scene.dpr = 1;
111833
+ }
110619
111834
  }
110620
111835
  Niivue.prototype.saveScene = function(filename = "") {
110621
111836
  function saveBlob(blob, name) {
@@ -110719,49 +111934,80 @@ Niivue.prototype.connectToServer = function(wsServerUrl, sessionName) {
110719
111934
  Niivue.prototype.setUpdateInterval = function() {
110720
111935
  this.interval$ = interval(300);
110721
111936
  this.interval$.subscribe(() => {
110722
- this.serverConnection$.next({
110723
- op: "update",
110724
- azimuth: this.scene.renderAzimuth,
110725
- elevation: this.scene.renderElevation,
110726
- clipPlane: this.scene.clipPlane,
110727
- zoom: this.volScaleMultiplier,
110728
- key: this.sessionKey
110729
- });
111937
+ this.serverConnection$.next(new NVMessage(UPDATE, new NVMesssageUpdateData(this.scene.renderAzimuth, this.scene.renderElevation, this.scene.clipPlane, this.volScaleMultiplier), this.sessionKey));
110730
111938
  });
110731
111939
  };
111940
+ Niivue.prototype.handleMessage = function(msg2, sessionCreatedCallback, sessionJoinedCallback) {
111941
+ switch (msg2["op"]) {
111942
+ case UPDATE:
111943
+ this.scene.renderAzimuth = msg2["azimuth"];
111944
+ this.scene.renderElevation = msg2["elevation"];
111945
+ this.volScaleMultiplier = msg2["zoom"];
111946
+ this.scene.clipPlane = msg2["clipPlane"];
111947
+ this.drawScene();
111948
+ break;
111949
+ case CREATE:
111950
+ console.log(msg2);
111951
+ if (!msg2["isError"]) {
111952
+ this.isInSession = true;
111953
+ this.sessionKey = msg2["key"];
111954
+ this.setUpdateInterval();
111955
+ }
111956
+ if (sessionCreatedCallback) {
111957
+ sessionCreatedCallback(msg2["message"], msg2["url"], msg2["key"], msg2["isError"], msg2["userKey"]);
111958
+ }
111959
+ break;
111960
+ case JOIN:
111961
+ this.isInSession = true;
111962
+ this.isController = msg2["isController"];
111963
+ if (this.isController) {
111964
+ this.setUpdateInterval();
111965
+ }
111966
+ if (sessionJoinedCallback) {
111967
+ sessionJoinedCallback(msg2["message"], msg2["url"], msg2["isController"], msg2["userKey"]);
111968
+ }
111969
+ break;
111970
+ case ADD_MESH_URL:
111971
+ this.addMeshFromUrl(msg2["urlMeshOptions"], false);
111972
+ break;
111973
+ case ADD_VOLUME_URL:
111974
+ this.addVolumeFromUrl(msg2["urlImageOptions"], false);
111975
+ break;
111976
+ case REMOVE_VOLUME_URL:
111977
+ {
111978
+ let volume = this.getMediaByUrl(msg2["url"]);
111979
+ if (volume) {
111980
+ this.removeVolume(volume, false);
111981
+ }
111982
+ }
111983
+ break;
111984
+ case REMOVE_MESH_URL: {
111985
+ let mesh = this.getMediaByUrl(msg2["url"]);
111986
+ if (mesh) {
111987
+ this.removeMesh(mesh, false);
111988
+ }
111989
+ }
111990
+ case SET_4D_VOL_INDEX:
111991
+ {
111992
+ let volume = this.getMediaByUrl(msg2["url"]);
111993
+ if (volume) {
111994
+ this.setFrame4D(volume.id, msg2["index"]);
111995
+ }
111996
+ }
111997
+ break;
111998
+ case UPDATE_IMAGE_OPTIONS: {
111999
+ let volume = this.getMediaByUrl(msg2["urlImageOptions"].url);
112000
+ if (volume) {
112001
+ volume.applyOptionsUpdate(msg2["urlImageOptions"]);
112002
+ this.updateGLVolume();
112003
+ }
112004
+ }
112005
+ }
112006
+ };
110732
112007
  Niivue.prototype.subscribeToServer = function(sessionCreatedCallback, sessionJoinedCallback) {
110733
112008
  this.serverConnection$.subscribe({
110734
112009
  next: (msg2) => {
110735
- switch (msg2["op"]) {
110736
- case "update":
110737
- this.scene.renderAzimuth = msg2["azimuth"];
110738
- this.scene.renderElevation = msg2["elevation"];
110739
- this.volScaleMultiplier = msg2["zoom"];
110740
- this.scene.clipPlane = msg2["clipPlane"];
110741
- this.drawScene();
110742
- break;
110743
- case "create":
110744
- console.log(msg2);
110745
- if (!msg2["isError"]) {
110746
- this.isInSession = true;
110747
- this.sessionKey = msg2["key"];
110748
- this.setUpdateInterval();
110749
- }
110750
- if (sessionCreatedCallback) {
110751
- sessionCreatedCallback(msg2["message"], msg2["url"], msg2["key"], msg2["isError"]);
110752
- }
110753
- break;
110754
- case "join":
110755
- this.isInSession = true;
110756
- this.isController = msg2["isController"];
110757
- if (this.isController) {
110758
- this.setUpdateInterval();
110759
- }
110760
- if (sessionJoinedCallback) {
110761
- sessionJoinedCallback(msg2["message"], msg2["url"], msg2["isController"]);
110762
- }
110763
- break;
110764
- }
112010
+ this.handleMessage(msg2, sessionCreatedCallback, sessionJoinedCallback);
110765
112011
  },
110766
112012
  error: (err2) => console.log(err2),
110767
112013
  complete: () => console.log("complete")
@@ -110770,17 +112016,12 @@ Niivue.prototype.subscribeToServer = function(sessionCreatedCallback, sessionJoi
110770
112016
  Niivue.prototype.createSession = function(wsServerUrl, sessionName, sessionCreatedCallback) {
110771
112017
  this.connectToServer(wsServerUrl, sessionName);
110772
112018
  this.subscribeToServer(sessionCreatedCallback);
110773
- this.serverConnection$.next({
110774
- op: "create"
110775
- });
112019
+ this.serverConnection$.next(new NVMessage(CREATE));
110776
112020
  };
110777
112021
  Niivue.prototype.joinSession = function(wsServerUrl, sessionName, key2, sessionJoinedCallback) {
110778
112022
  this.connectToServer(wsServerUrl, sessionName);
110779
112023
  this.subscribeToServer(null, sessionJoinedCallback);
110780
- this.serverConnection$.next({
110781
- op: "join",
110782
- key: key2
110783
- });
112024
+ this.serverConnection$.next(new NVMessage(JOIN, key2));
110784
112025
  };
110785
112026
  Niivue.prototype.closeSession = function() {
110786
112027
  this.interval$.complete();
@@ -110794,8 +112035,19 @@ Niivue.prototype.arrayEquals = function(a, b) {
110794
112035
  Niivue.prototype.resizeListener = function() {
110795
112036
  this.canvas.style.width = "100%";
110796
112037
  this.canvas.style.height = "100%";
110797
- this.canvas.width = this.canvas.offsetWidth;
110798
- this.canvas.height = this.canvas.offsetHeight;
112038
+ if (this.opts.isHighResolutionCapable) {
112039
+ this.scene.dpr = window.devicePixelRatio || 1;
112040
+ console.log("devicePixelRatio: " + this.scene.dpr);
112041
+ } else {
112042
+ this.scene.dpr = 1;
112043
+ }
112044
+ if (this.canvas.parentElement.hasOwnProperty("width")) {
112045
+ this.canvas.width = this.canvas.parentElement.width * this.scene.dpr;
112046
+ this.canvas.height = this.canvas.parentElement.height * this.scene.dpr;
112047
+ } else {
112048
+ this.canvas.width = this.canvas.offsetWidth * this.scene.dpr;
112049
+ this.canvas.height = this.canvas.offsetHeight * this.scene.dpr;
112050
+ }
110799
112051
  this.drawScene();
110800
112052
  };
110801
112053
  Niivue.prototype.getRelativeMousePosition = function(event, target) {
@@ -110809,8 +112061,6 @@ Niivue.prototype.getRelativeMousePosition = function(event, target) {
110809
112061
  Niivue.prototype.getNoPaddingNoBorderCanvasRelativeMousePosition = function(event, target) {
110810
112062
  target = target || event.target;
110811
112063
  var pos = this.getRelativeMousePosition(event, target);
110812
- pos.x = pos.x * target.width / target.clientWidth;
110813
- pos.y = pos.y * target.height / target.clientHeight;
110814
112064
  return pos;
110815
112065
  };
110816
112066
  Niivue.prototype.mouseContextMenuListener = function(e) {
@@ -110835,10 +112085,11 @@ Niivue.prototype.mouseLeftButtonHandler = function(e) {
110835
112085
  this.mouseDown(pos.x, pos.y);
110836
112086
  };
110837
112087
  Niivue.prototype.mouseRightButtonHandler = function(e) {
110838
- this.isDragging = true;
110839
112088
  let pos = this.getNoPaddingNoBorderCanvasRelativeMousePosition(e, this.gl.canvas);
110840
- this.dragStart[0] = pos.x;
110841
- this.dragStart[1] = pos.y;
112089
+ this.setDragStart(pos.x, pos.y);
112090
+ if (!this.isDragging)
112091
+ this.scene.pan2DxyzmmAtMouseDown = this.scene.pan2Dxyzmm.slice();
112092
+ this.isDragging = true;
110842
112093
  this.dragClipPlaneStartDepthAziElev = this.scene.clipPlaneDepthAziElev;
110843
112094
  return;
110844
112095
  };
@@ -110870,13 +112121,11 @@ Niivue.prototype.calculateNewRange = function(volIdx = 0) {
110870
112121
  if (frac[0] < 0)
110871
112122
  return;
110872
112123
  let endVox = this.frac2vox(frac, volIdx);
110873
- let hi = -Number.MAX_VALUE, lo = Number.MAX_VALUE;
110874
- let xrange;
110875
- let yrange;
110876
- let zrange;
110877
- xrange = this.calculateMinMaxVoxIdx([startVox[0], endVox[0]]);
110878
- yrange = this.calculateMinMaxVoxIdx([startVox[1], endVox[1]]);
110879
- zrange = this.calculateMinMaxVoxIdx([startVox[2], endVox[2]]);
112124
+ let hi = -Number.MAX_VALUE;
112125
+ let lo = Number.MAX_VALUE;
112126
+ let xrange = this.calculateMinMaxVoxIdx([startVox[0], endVox[0]]);
112127
+ let yrange = this.calculateMinMaxVoxIdx([startVox[1], endVox[1]]);
112128
+ let zrange = this.calculateMinMaxVoxIdx([startVox[2], endVox[2]]);
110880
112129
  if (startVox[0] - endVox[0] === 0) {
110881
112130
  xrange[1] = startVox[0] + 1;
110882
112131
  } else if (startVox[1] - endVox[1] === 0) {
@@ -110923,6 +112172,8 @@ Niivue.prototype.mouseUpListener = function() {
110923
112172
  this.drawPenAxCorSag = -1;
110924
112173
  if (this.isDragging) {
110925
112174
  this.isDragging = false;
112175
+ if (this.opts.isDragForPanZoom)
112176
+ return;
110926
112177
  if (this.opts.isDragShowsMeasurementTool)
110927
112178
  return;
110928
112179
  this.calculateNewRange();
@@ -110939,7 +112190,26 @@ Niivue.prototype.checkMultitouch = function(e) {
110939
112190
  };
110940
112191
  Niivue.prototype.touchStartListener = function(e) {
110941
112192
  e.preventDefault();
112193
+ if (!this.touchTimer) {
112194
+ this.touchTimer = setTimeout(() => {
112195
+ this.resetBriCon(e);
112196
+ }, this.opts.longTouchTimeout);
112197
+ }
110942
112198
  this.scene.touchdown = true;
112199
+ this.currentTouchTime = new Date().getTime();
112200
+ let timeSinceTouch = this.currentTouchTime - this.lastTouchTime;
112201
+ if (timeSinceTouch < this.opts.doubleTouchTimeout && timeSinceTouch > 0) {
112202
+ this.doubleTouch = true;
112203
+ this.setDragStart(e.targetTouches[0].clientX - e.target.getBoundingClientRect().left, e.targetTouches[0].clientY - e.target.getBoundingClientRect().top);
112204
+ this.resetBriCon(e);
112205
+ this.lastTouchTime = this.currentTouchTime;
112206
+ return;
112207
+ } else {
112208
+ this.doubleTouch = false;
112209
+ this.setDragStart(0, 0);
112210
+ this.setDragEnd(0, 0);
112211
+ this.lastTouchTime = this.currentTouchTime;
112212
+ }
110943
112213
  if (this.scene.touchdown && e.touches.length < 2) {
110944
112214
  this.multiTouchGesture = false;
110945
112215
  } else {
@@ -110947,10 +112217,23 @@ Niivue.prototype.touchStartListener = function(e) {
110947
112217
  }
110948
112218
  setTimeout(this.checkMultitouch.bind(this), 1, e);
110949
112219
  };
110950
- Niivue.prototype.touchEndListener = function() {
112220
+ Niivue.prototype.touchEndListener = function(e) {
112221
+ e.preventDefault();
110951
112222
  this.scene.touchdown = false;
110952
112223
  this.lastTwoTouchDistance = 0;
110953
112224
  this.multiTouchGesture = false;
112225
+ if (this.touchTimer) {
112226
+ clearTimeout(this.touchTimer);
112227
+ this.touchTimer = null;
112228
+ }
112229
+ if (this.isDragging) {
112230
+ this.isDragging = false;
112231
+ if (this.opts.isDragShowsMeasurementTool)
112232
+ return;
112233
+ this.calculateNewRange();
112234
+ this.refreshLayers(this.volumes[0], 0, this.volumes.length);
112235
+ }
112236
+ this.drawScene();
110954
112237
  };
110955
112238
  Niivue.prototype.mouseMoveListener = function(e) {
110956
112239
  if (this.scene.mousedown) {
@@ -110959,8 +112242,7 @@ Niivue.prototype.mouseMoveListener = function(e) {
110959
112242
  this.mouseClick(pos.x, pos.y);
110960
112243
  this.mouseMove(pos.x, pos.y);
110961
112244
  } else if (this.scene.mouseButtonRightDown) {
110962
- this.dragEnd[0] = pos.x;
110963
- this.dragEnd[1] = pos.y;
112245
+ this.setDragEnd(pos.x, pos.y);
110964
112246
  }
110965
112247
  this.drawScene();
110966
112248
  this.scene.prevX = this.scene.currX;
@@ -110968,12 +112250,24 @@ Niivue.prototype.mouseMoveListener = function(e) {
110968
112250
  }
110969
112251
  };
110970
112252
  Niivue.prototype.resetBriCon = function(msg2 = null) {
112253
+ if (this.isDragging)
112254
+ return;
110971
112255
  let isRender = false;
110972
- if (this.sliceType === this.sliceTypeRender) {
112256
+ if (this.sliceType === this.sliceTypeRender)
110973
112257
  isRender = true;
110974
- }
112258
+ let x2 = 0;
112259
+ let y = 0;
110975
112260
  if (msg2 !== null) {
110976
- if (this.inRenderTile(msg2.offsetX, msg2.offsetY) >= 0)
112261
+ if (msg2.targetTouches !== void 0) {
112262
+ x2 = msg2.targetTouches[0].clientX - msg2.target.getBoundingClientRect().left;
112263
+ y = msg2.targetTouches[0].clientY - msg2.target.getBoundingClientRect().top;
112264
+ } else {
112265
+ x2 = msg2.offsetX;
112266
+ y = msg2.offsetY;
112267
+ }
112268
+ x2 *= this.scene.dpr;
112269
+ y *= this.scene.dpr;
112270
+ if (this.inRenderTile(x2, y) >= 0)
110977
112271
  isRender = true;
110978
112272
  }
110979
112273
  if (isRender) {
@@ -110982,15 +112276,37 @@ Niivue.prototype.resetBriCon = function(msg2 = null) {
110982
112276
  this.drawScene();
110983
112277
  return;
110984
112278
  }
112279
+ if (this.doubleTouch)
112280
+ return;
110985
112281
  this.volumes[0].cal_min = this.volumes[0].robust_min;
110986
112282
  this.volumes[0].cal_max = this.volumes[0].robust_max;
110987
112283
  this.opts.onIntensityChange(this.volumes[0]);
110988
112284
  this.refreshLayers(this.volumes[0], 0, this.volumes.length);
110989
112285
  this.drawScene();
110990
112286
  };
112287
+ Niivue.prototype.setDragStart = function(x2, y) {
112288
+ x2 *= this.scene.dpr;
112289
+ y *= this.scene.dpr;
112290
+ this.dragStart[0] = x2;
112291
+ this.dragStart[1] = y;
112292
+ };
112293
+ Niivue.prototype.setDragEnd = function(x2, y) {
112294
+ x2 *= this.scene.dpr;
112295
+ y *= this.scene.dpr;
112296
+ this.dragEnd[0] = x2;
112297
+ this.dragEnd[1] = y;
112298
+ };
110991
112299
  Niivue.prototype.touchMoveListener = function(e) {
110992
112300
  if (this.scene.touchdown && e.touches.length < 2) {
110993
112301
  var rect = this.canvas.getBoundingClientRect();
112302
+ if (!this.isDragging)
112303
+ this.scene.pan2DxyzmmAtMouseDown = this.scene.pan2Dxyzmm.slice();
112304
+ this.isDragging = true;
112305
+ if (this.doubleTouch && this.isDragging) {
112306
+ this.setDragEnd(e.targetTouches[0].clientX - e.target.getBoundingClientRect().left, e.targetTouches[0].clientY - e.target.getBoundingClientRect().top);
112307
+ this.drawScene();
112308
+ return;
112309
+ }
110994
112310
  this.mouseClick(e.touches[0].clientX - rect.left, e.touches[0].clientY - rect.top);
110995
112311
  this.mouseMove(e.touches[0].clientX - rect.left, e.touches[0].clientY - rect.top);
110996
112312
  } else {
@@ -111090,6 +112406,9 @@ Niivue.prototype.keyDownListener = function(e) {
111090
112406
  Niivue.prototype.wheelListener = function(e) {
111091
112407
  e.preventDefault();
111092
112408
  e.stopPropagation();
112409
+ if (this.thumbnailVisible) {
112410
+ return;
112411
+ }
111093
112412
  var rect = this.canvas.getBoundingClientRect();
111094
112413
  if (e.deltaY < 0) {
111095
112414
  this.sliceScroll2D(-0.01, e.clientX - rect.left, e.clientY - rect.top);
@@ -111133,6 +112452,77 @@ Niivue.prototype.getFileExt = function(fullname, upperCase = true) {
111133
112452
  }
111134
112453
  return upperCase ? ext : ext.toLowerCase();
111135
112454
  };
112455
+ Niivue.prototype.loadVolumeFromUrl = async function(imageOptions) {
112456
+ let volume = await NVImage.loadFromUrl(imageOptions);
112457
+ return volume;
112458
+ };
112459
+ Niivue.prototype.notifySubscribersOfOptionChange = function(volume) {
112460
+ if (this.isInSession) {
112461
+ if (this.mediaUrlMap.has(volume)) {
112462
+ let imageOptions = volume.getImageOptions();
112463
+ imageOptions.url = this.mediaUrlMap.get(volume);
112464
+ this.serverConnection$.next(new NVMessage(UPDATE_IMAGE_OPTIONS, imageOptions, this.sessionKey));
112465
+ console.log("update called");
112466
+ }
112467
+ }
112468
+ };
112469
+ Niivue.prototype.addVolumeFromUrl = async function(imageOptions, notifySubscribers = true) {
112470
+ let volume = await this.loadVolumeFromUrl(imageOptions);
112471
+ this.addVolume(volume);
112472
+ if (!this.mediaUrlMap.has(volume) && imageOptions.url) {
112473
+ this.mediaUrlMap.set(volume, imageOptions.url);
112474
+ if (this.isInSession && notifySubscribers) {
112475
+ this.serverConnection$.next(new NVMessage(ADD_VOLUME_URL, imageOptions, this.sessionKey));
112476
+ }
112477
+ }
112478
+ return volume;
112479
+ };
112480
+ Niivue.prototype.getMediaByUrl = function(url) {
112481
+ return [...this.mediaUrlMap.entries()].filter((v) => v[1] == url).map((v) => v[0]).pop();
112482
+ };
112483
+ Niivue.prototype.removeVolumeByUrl = function(url) {
112484
+ let volume = this.getMediaByUrl(url);
112485
+ if (volume) {
112486
+ this.removeVolume(volume);
112487
+ }
112488
+ };
112489
+ Niivue.prototype.readDirectory = function(directory) {
112490
+ let reader = directory.createReader();
112491
+ let allEntiresInDir = [];
112492
+ let getFileObjects = async (fileSystemEntries) => {
112493
+ let allFileObects = [];
112494
+ async function getFile(fileEntry) {
112495
+ try {
112496
+ return await new Promise((resolve, reject) => fileEntry.file(resolve, reject));
112497
+ } catch (err2) {
112498
+ console.log(err2);
112499
+ }
112500
+ }
112501
+ for (let i2 = 0; i2 < fileSystemEntries.length; i2++) {
112502
+ allFileObects.push(await getFile(fileSystemEntries[i2]));
112503
+ }
112504
+ return allFileObects;
112505
+ };
112506
+ let readEntries = () => {
112507
+ reader.readEntries(async (entries) => {
112508
+ if (entries.length) {
112509
+ allEntiresInDir = allEntiresInDir.concat(entries);
112510
+ readEntries();
112511
+ } else {
112512
+ let allFileObects = await getFileObjects(allEntiresInDir);
112513
+ let volume = await NVImage.loadFromFile({
112514
+ file: allFileObects,
112515
+ name: directory.name,
112516
+ urlImgData: null,
112517
+ isDICOMDIR: true
112518
+ });
112519
+ this.addVolume(volume);
112520
+ }
112521
+ });
112522
+ };
112523
+ readEntries();
112524
+ return allEntiresInDir;
112525
+ };
111136
112526
  Niivue.prototype.dropListener = async function(e) {
111137
112527
  e.stopPropagation();
111138
112528
  e.preventDefault();
@@ -111142,8 +112532,15 @@ Niivue.prototype.dropListener = async function(e) {
111142
112532
  const dt = e.dataTransfer;
111143
112533
  const url = dt.getData("text/uri-list");
111144
112534
  if (url) {
111145
- let volume = await NVImage.loadFromUrl({ url });
111146
- this.setVolume(volume);
112535
+ let imageOptions = new NVImageFromUrlOptions(url);
112536
+ let ext = this.getFileExt(url);
112537
+ console.log("dropped ext");
112538
+ console.log(ext);
112539
+ if (MESH_EXTENSIONS.includes(ext)) {
112540
+ this.addMeshFromUrl({ url });
112541
+ } else {
112542
+ this.addVolumeFromUrl(imageOptions);
112543
+ }
111147
112544
  } else {
111148
112545
  const items = dt.items;
111149
112546
  if (items.length > 0) {
@@ -111154,6 +112551,7 @@ Niivue.prototype.dropListener = async function(e) {
111154
112551
  }
111155
112552
  for (const item of items) {
111156
112553
  const entry = item.getAsEntry || item.webkitGetAsEntry();
112554
+ console.log(entry);
111157
112555
  if (entry.isFile) {
111158
112556
  let ext = this.getFileExt(entry.name);
111159
112557
  if (ext === "PNG") {
@@ -111176,7 +112574,7 @@ Niivue.prototype.dropListener = async function(e) {
111176
112574
  if (entry.name.lastIndexOf("BRIK") !== -1) {
111177
112575
  continue;
111178
112576
  }
111179
- if (ext === "ASC" || ext === "DFS" || ext === "FSM" || ext === "PIAL" || ext === "ORIG" || ext === "INFLATED" || ext === "SMOOTHWM" || ext === "SPHERE" || ext === "WHITE" || ext === "GII" || ext === "MZ3" || ext === "NV" || ext === "OBJ" || ext === "OFF" || ext === "PLY" || ext === "SRF" || ext === "STL" || ext === "TCK" || ext === "TRACT" || ext === "TRK" || ext === "TRX" || ext === "VTK" || ext === "X3D") {
112577
+ if (MESH_EXTENSIONS.includes(ext)) {
111180
112578
  entry.file(async (file) => {
111181
112579
  let mesh = await NVMesh.loadFromFile({
111182
112580
  file,
@@ -111205,8 +112603,9 @@ Niivue.prototype.dropListener = async function(e) {
111205
112603
  this.addVolume(volume);
111206
112604
  }
111207
112605
  });
111208
- } else if (entry.isDirectory)
111209
- ;
112606
+ } else if (entry.isDirectory) {
112607
+ this.readDirectory(entry);
112608
+ }
111210
112609
  }
111211
112610
  }
111212
112611
  }
@@ -111236,6 +112635,15 @@ Niivue.prototype.setSliceMM = function(isSliceMM) {
111236
112635
  Niivue.prototype.getRadiologicalConvention = function() {
111237
112636
  return this.opts.isRadiologicalConvention;
111238
112637
  };
112638
+ Niivue.prototype.setHighResolutionCapable = function(isHighResolutionCapable) {
112639
+ this.opts.isHighResolutionCapable = isHighResolutionCapable;
112640
+ if (!this.opts.isHighResolutionCapable) {
112641
+ this.scene.dpr = 1;
112642
+ }
112643
+ console.log("HighDPI feature is experimental");
112644
+ this.resizeListener();
112645
+ this.drawScene();
112646
+ };
111239
112647
  Niivue.prototype.addVolume = function(volume) {
111240
112648
  this.volumes.push(volume);
111241
112649
  let idx = this.volumes.length === 1 ? 0 : this.volumes.length - 1;
@@ -111332,12 +112740,12 @@ Niivue.prototype.drawAddUndoBitmap = async function(fnm) {
111332
112740
  return false;
111333
112741
  }
111334
112742
  this.currentDrawUndoBitmap++;
111335
- if (this.currentDrawUndoBitmap >= this.maxDrawUndoBitmaps)
112743
+ if (this.currentDrawUndoBitmap >= this.opts.maxDrawUndoBitmaps)
111336
112744
  this.currentDrawUndoBitmap = 0;
111337
112745
  this.drawUndoBitmaps[this.currentDrawUndoBitmap] = encodeRLE(this.drawBitmap);
111338
112746
  };
111339
112747
  Niivue.prototype.drawClearAllUndoBitmaps = async function() {
111340
- this.currentDrawUndoBitmap = this.maxDrawUndoBitmaps;
112748
+ this.currentDrawUndoBitmap = this.opts.maxDrawUndoBitmaps;
111341
112749
  if (this.drawUndoBitmaps.length < 1)
111342
112750
  return;
111343
112751
  for (let i2 = this.drawUndoBitmaps.length - 1; i2 >= 0; i2--)
@@ -111364,7 +112772,7 @@ Niivue.prototype.loadDrawing = async function(fnm) {
111364
112772
  if (this.drawBitmap)
111365
112773
  console.log("Overwriting open drawing!");
111366
112774
  this.drawClearAllUndoBitmaps();
111367
- let volume = await NVImage.loadFromUrl({ url: fnm });
112775
+ let volume = await this.addVolumeFromUrl(new NVImageFromUrlOptions(fnm));
111368
112776
  let dims = volume.hdr.dims;
111369
112777
  if (dims[1] !== this.back.hdr.dims[1] || dims[2] !== this.back.hdr.dims[2] || dims[3] !== this.back.hdr.dims[3]) {
111370
112778
  console.log("drawing dimensions do not match background image");
@@ -111502,6 +112910,8 @@ Niivue.prototype.saveImage = async function(fnm, isSaveDrawing = false) {
111502
112910
  return true;
111503
112911
  };
111504
112912
  Niivue.prototype.getMeshIndexByID = function(id) {
112913
+ if (typeof id === "number")
112914
+ return id;
111505
112915
  let n = this.meshes.length;
111506
112916
  for (let i2 = 0; i2 < n; i2++) {
111507
112917
  let id_i = this.meshes[i2].id;
@@ -111542,10 +112952,6 @@ Niivue.prototype.setPan2Dxyzmm = function(xyzmmZoom) {
111542
112952
  this.scene.pan2Dxyzmm = xyzmmZoom;
111543
112953
  this.drawScene();
111544
112954
  };
111545
- Niivue.prototype.setScale2D = function(zoomFactor) {
111546
- this.scene.scale2D = zoomFactor;
111547
- this.drawScene();
111548
- };
111549
112955
  Niivue.prototype.setRenderAzimuthElevation = function(a, e) {
111550
112956
  this.scene.renderAzimuth = a;
111551
112957
  this.scene.renderElevation = e;
@@ -111617,11 +113023,43 @@ Niivue.prototype.setMesh = function(mesh, toIndex = 0) {
111617
113023
  log.debug(m.name);
111618
113024
  });
111619
113025
  };
111620
- Niivue.prototype.removeVolume = function(volume) {
113026
+ Niivue.prototype.removeVolume = function(volume, notifySubscribers = true) {
111621
113027
  this.setVolume(volume, -1);
113028
+ if (this.mediaUrlMap.has(volume)) {
113029
+ let url = this.mediaUrlMap.get(volume);
113030
+ if (notifySubscribers && this.isInSession) {
113031
+ this.serverConnection$.next(new NVMessage(REMOVE_VOLUME_URL, url, this.sessionKey));
113032
+ }
113033
+ this.mediaUrlMap.delete(volume);
113034
+ }
113035
+ };
113036
+ Niivue.prototype.removeVolumeByIndex = function(index) {
113037
+ if (index >= this.volumes.length) {
113038
+ throw "Index of volume out of bounds";
113039
+ }
113040
+ this.removeVolume(this.volumes[index]);
113041
+ };
113042
+ Niivue.prototype.removeVolumeByIndex = function(index) {
113043
+ if (index >= this.volumes.length) {
113044
+ throw "Index of volume out of bounds";
113045
+ }
113046
+ this.removeVolume(this.volumes[index]);
113047
+ };
113048
+ Niivue.prototype.removeVolumeByIndex = function(index) {
113049
+ if (index >= this.volumes.length) {
113050
+ throw "Index of volume out of bounds";
113051
+ }
113052
+ this.removeVolume(this.volumes[index]);
111622
113053
  };
111623
- Niivue.prototype.removeMesh = function(mesh) {
113054
+ Niivue.prototype.removeMesh = function(mesh, notifySubscribers = true) {
111624
113055
  this.setMesh(mesh, -1);
113056
+ let url = this.mediaUrlMap.get(mesh);
113057
+ if (url) {
113058
+ this.mediaUrlMap.delete(mesh);
113059
+ if (notifySubscribers && this.isInSession) {
113060
+ this.serverConnection$.next(new NVMessage(REMOVE_MESH_URL, url, this.sessionKey));
113061
+ }
113062
+ }
111625
113063
  };
111626
113064
  Niivue.prototype.moveVolumeToBottom = function(volume) {
111627
113065
  this.setVolume(volume, 0);
@@ -111638,11 +113076,15 @@ Niivue.prototype.moveVolumeToTop = function(volume) {
111638
113076
  this.setVolume(volume, this.volumes.length - 1);
111639
113077
  };
111640
113078
  Niivue.prototype.mouseDown = function mouseDown(x2, y) {
113079
+ x2 *= this.scene.dpr;
113080
+ y *= this.scene.dpr;
111641
113081
  if (this.inRenderTile(x2, y) < 0)
111642
113082
  return;
111643
113083
  this.mousePos = [x2, y];
111644
113084
  };
111645
113085
  Niivue.prototype.mouseMove = function mouseMove(x2, y) {
113086
+ x2 *= this.scene.dpr;
113087
+ y *= this.scene.dpr;
111646
113088
  if (this.inRenderTile(x2, y) < 0)
111647
113089
  return;
111648
113090
  this.scene.renderAzimuth += x2 - this.mousePos[0];
@@ -111676,6 +113118,11 @@ Niivue.prototype.setCrosshairColor = function(color) {
111676
113118
  this.opts.crosshairColor = color;
111677
113119
  this.drawScene();
111678
113120
  };
113121
+ Niivue.prototype.setCrosshairWidth = function(crosshairWidth) {
113122
+ this.opts.crosshairWidth = crosshairWidth;
113123
+ this.crosshairs3D.mm[0] = NaN;
113124
+ this.drawScene();
113125
+ };
111679
113126
  Niivue.prototype.setDrawingEnabled = function(trueOrFalse) {
111680
113127
  this.opts.drawingEnabled = trueOrFalse;
111681
113128
  if (this.opts.drawingEnabled) {
@@ -111691,14 +113138,19 @@ Niivue.prototype.setPenValue = function(penValue, isFilledPen = false) {
111691
113138
  };
111692
113139
  Niivue.prototype.setDrawOpacity = function(opacity) {
111693
113140
  this.drawOpacity = opacity;
111694
- this.sliceShader.use(this.gl);
111695
- this.gl.uniform1f(this.sliceShader.uniforms["drawOpacity"], this.drawOpacity);
111696
113141
  this.drawScene();
111697
113142
  };
111698
113143
  Niivue.prototype.setSelectionBoxColor = function(color) {
111699
113144
  this.opts.selectionBoxColor = color;
111700
113145
  };
111701
113146
  Niivue.prototype.sliceScroll2D = function(posChange, x2, y, isDelta = true) {
113147
+ if (posChange !== 0 && this.opts.isDragForPanZoom) {
113148
+ let zoom = this.scene.pan2Dxyzmm[3] * (1 + 10 * posChange);
113149
+ zoom = Math.round(zoom * 10) / 10;
113150
+ this.scene.pan2Dxyzmm[3] = zoom;
113151
+ this.drawScene();
113152
+ return;
113153
+ }
111702
113154
  this.mouseClick(x2, y, posChange, isDelta);
111703
113155
  };
111704
113156
  Niivue.prototype.setSliceType = function(st) {
@@ -111721,7 +113173,7 @@ Niivue.prototype.setScale = function(scale2) {
111721
113173
  Niivue.prototype.setClipPlaneColor = function(color) {
111722
113174
  this.opts.clipPlaneColor = color;
111723
113175
  this.renderShader.use(this.gl);
111724
- this.gl.uniform4fv(this.renderShader.uniforms["clipPlaneColor"], this.opts.clipPlaneColor);
113176
+ this.gl.uniform4fv(this.renderShader.clipPlaneClrLoc, this.opts.clipPlaneColor);
111725
113177
  this.drawScene();
111726
113178
  };
111727
113179
  Niivue.prototype.overlayRGBA = function(volume) {
@@ -111782,7 +113234,7 @@ Niivue.prototype.loadVolumes = async function(volumeList) {
111782
113234
  this.scene.loading$.next(false);
111783
113235
  for (let i2 = 0; i2 < volumeList.length; i2++) {
111784
113236
  this.scene.loading$.next(true);
111785
- let volume = await NVImage.loadFromUrl({
113237
+ let imageOptions = {
111786
113238
  url: volumeList[i2].url,
111787
113239
  name: volumeList[i2].name,
111788
113240
  colorMap: volumeList[i2].colorMap,
@@ -111792,12 +113244,30 @@ Niivue.prototype.loadVolumes = async function(volumeList) {
111792
113244
  cal_min: volumeList[i2].cal_min,
111793
113245
  cal_max: volumeList[i2].cal_max,
111794
113246
  trustCalMinMax: this.opts.trustCalMinMax
111795
- });
113247
+ };
113248
+ await this.addVolumeFromUrl(imageOptions);
111796
113249
  this.scene.loading$.next(false);
111797
- this.addVolume(volume);
111798
113250
  }
111799
113251
  return this;
111800
113252
  };
113253
+ Niivue.prototype.addMeshFromUrl = async function(meshOptions, notifySubscribers = true) {
113254
+ let mesh = await this.loadMeshFromUrl(meshOptions);
113255
+ this.addMesh(mesh);
113256
+ if (!this.mediaUrlMap.has(mesh) && meshOptions.url) {
113257
+ this.mediaUrlMap.set(mesh, meshOptions.url);
113258
+ if (this.isInSession && notifySubscribers) {
113259
+ this.serverConnection$.next(new NVMessage(ADD_MESH_URL, meshOptions, this.sessionKey));
113260
+ }
113261
+ }
113262
+ return mesh;
113263
+ };
113264
+ Niivue.prototype.loadMeshFromUrl = async function(meshOptions) {
113265
+ let options = new NVMeshFromUrlOptions();
113266
+ options.gl = this.gl;
113267
+ Object.assign(options, meshOptions);
113268
+ let mesh = await NVMesh.loadFromUrl(options);
113269
+ return mesh;
113270
+ };
111801
113271
  Niivue.prototype.loadMeshes = async function(meshList) {
111802
113272
  this.on("loading", (isLoading) => {
111803
113273
  if (isLoading) {
@@ -111819,17 +113289,8 @@ Niivue.prototype.loadMeshes = async function(meshList) {
111819
113289
  this.scene.loading$.next(false);
111820
113290
  for (let i2 = 0; i2 < meshList.length; i2++) {
111821
113291
  this.scene.loading$.next(true);
111822
- let mesh = await NVMesh.loadFromUrl({
111823
- url: meshList[i2].url,
111824
- gl: this.gl,
111825
- name: meshList[i2].name,
111826
- opacity: meshList[i2].opacity,
111827
- rgba255: meshList[i2].rgba255,
111828
- visible: meshList[i2].visible,
111829
- layers: meshList[i2].layers
111830
- });
113292
+ await this.addMeshFromUrl(meshList[i2]);
111831
113293
  this.scene.loading$.next(false);
111832
- this.addMesh(mesh);
111833
113294
  }
111834
113295
  this.drawScene();
111835
113296
  return this;
@@ -111843,8 +113304,6 @@ Niivue.prototype.loadConnectome = async function(json) {
111843
113304
  this.loadingText = this.opts.loadingText;
111844
113305
  }
111845
113306
  });
111846
- if (!this.initialized)
111847
- ;
111848
113307
  this.meshes = [];
111849
113308
  this.gl.clearColor(0, 0, 0, 1);
111850
113309
  this.gl.clear(this.gl.COLOR_BUFFER_BIT);
@@ -112480,33 +113939,53 @@ Niivue.prototype.loadDefaultFont = async function() {
112480
113939
  Niivue.prototype.initText = async function() {
112481
113940
  this.fontShader = new Shader(this.gl, vertFontShader, fragFontShader);
112482
113941
  this.fontShader.use(this.gl);
113942
+ this.fontShader.screenPxRangeLoc = this.fontShader.uniforms["screenPxRange"];
113943
+ this.fontShader.fontColorLoc = this.fontShader.uniforms["fontColor"];
113944
+ this.fontShader.canvasWidthHeightLoc = this.fontShader.uniforms["canvasWidthHeight"];
113945
+ this.fontShader.leftTopWidthHeightLoc = this.fontShader.uniforms["leftTopWidthHeight"];
113946
+ this.fontShader.uvLeftTopWidthHeightLoc = this.fontShader.uniforms["uvLeftTopWidthHeight"];
112483
113947
  await this.loadDefaultFont();
112484
113948
  this.drawLoadingText(this.loadingText);
112485
113949
  };
112486
- Niivue.prototype.setMeshShader = function(meshShaderNameOrNumber = 2) {
112487
- this.gl.deleteProgram(this.meshShader.program);
113950
+ Niivue.prototype.meshShaderNameToNumber = function(meshShaderName = "Phong") {
113951
+ let name = meshShaderName.toLowerCase();
113952
+ for (var i2 = 0; i2 < this.meshShaders.length; i2++) {
113953
+ if (this.meshShaders[i2].Name.toLowerCase() === name)
113954
+ return i2;
113955
+ }
113956
+ i2 = -1;
113957
+ };
113958
+ Niivue.prototype.setMeshShader = function(id, meshShaderNameOrNumber = 2) {
112488
113959
  let num = 0;
112489
113960
  if (typeof meshShaderNameOrNumber === "number")
112490
113961
  num = meshShaderNameOrNumber;
112491
113962
  else {
112492
- let name = meshShaderNameOrNumber.toLowerCase();
112493
- for (var i2 = 0; i2 < this.meshShaders.length; i2++) {
112494
- if (this.meshShaders[i2].Name.toLowerCase() === name) {
112495
- num = i2;
112496
- break;
112497
- }
112498
- }
113963
+ num = this.meshShaderNameToNumber(meshShaderNameOrNumber);
112499
113964
  }
112500
113965
  num = Math.min(num, this.meshShaders.length - 1);
112501
113966
  num = Math.max(num, 0);
112502
- this.meshShader = new Shader(this.gl, vertMeshShader, this.meshShaders[num].Frag);
113967
+ let idx = this.getMeshIndexByID(id);
113968
+ if (idx >= this.meshes.length) {
113969
+ console.log("Unable to change shader until mesh is loaded (maybe you need async)");
113970
+ return;
113971
+ }
113972
+ this.meshes[idx].meshShaderIndex = num;
112503
113973
  this.updateGLVolume();
112504
113974
  };
112505
- Niivue.prototype.setCustomMeshShader = function(fragmentShaderText = "") {
112506
- if (fragmentShaderText.length < 1)
112507
- fragmentShaderText = this.meshShaders[0].Frag;
112508
- this.meshShader = new Shader(this.gl, vertMeshShader, fragmentShaderText);
112509
- this.updateGLVolume();
113975
+ Niivue.prototype.setCustomMeshShader = function(fragmentShaderText = "", name = "Custom") {
113976
+ let num = this.meshShaderNameToNumber(name);
113977
+ if (num >= 0) {
113978
+ this.gl.deleteProgram(this.meshShaders[num].shader.program);
113979
+ this.meshShaders.splice(num, 1);
113980
+ }
113981
+ let m = [];
113982
+ m.Name = name;
113983
+ m.Frag = fragmentShaderText;
113984
+ m.shader = new Shader(this.gl, vertMeshShader, m.Frag);
113985
+ m.shader.use(this.gl);
113986
+ m.shader.mvpLoc = m.shader.uniforms["mvpMtx"];
113987
+ this.meshShaders.push(m);
113988
+ return this.meshShaders.length - 1;
112510
113989
  };
112511
113990
  Niivue.prototype.meshShaderNames = function(sort = true) {
112512
113991
  let cm = [];
@@ -112551,35 +114030,89 @@ Niivue.prototype.init = async function() {
112551
114030
  this.gl.enableVertexAttribArray(0);
112552
114031
  this.gl.vertexAttribPointer(0, 3, this.gl.FLOAT, false, 0, 0);
112553
114032
  this.gl.bindVertexArray(this.unusedVAO);
112554
- this.pickingShader = new Shader(this.gl, vertRenderShader, fragVolumePickingShader);
112555
- this.pickingShader.use(this.gl);
112556
- this.gl.uniform1i(this.pickingShader.uniforms["volume"], 0);
112557
- this.gl.uniform1i(this.pickingShader.uniforms["overlay"], 2);
112558
- this.pickingShader.mvpUniformLoc = this.pickingShader.uniforms["mvpMtx"];
112559
- this.pickingShader.rayDirUniformLoc = this.pickingShader.uniforms["rayDir"];
112560
- this.pickingShader.clipPlaneUniformLoc = this.pickingShader.uniforms["clipPlane"];
114033
+ this.pickingMeshShader = new Shader(this.gl, vertMeshShader, fragMeshDepthShader);
114034
+ this.pickingMeshShader.use(this.gl);
114035
+ this.pickingMeshShader.mvpLoc = this.pickingMeshShader.uniforms["mvpMtx"];
114036
+ this.pickingImageShader = new Shader(this.gl, vertRenderShader, fragVolumePickingShader);
114037
+ this.pickingImageShader.use(this.gl);
114038
+ this.pickingImageShader.drawOpacityLoc = this.pickingImageShader.uniforms["drawOpacity"];
114039
+ this.pickingImageShader.backgroundMasksOverlaysLoc = this.pickingImageShader.uniforms["backgroundMasksOverlays"];
114040
+ this.pickingImageShader.mvpLoc = this.pickingImageShader.uniforms["mvpMtx"];
114041
+ this.gl.uniform1i(this.pickingImageShader.uniforms["volume"], 0);
114042
+ this.gl.uniform1i(this.pickingImageShader.uniforms["overlay"], 2);
114043
+ this.gl.uniform1i(this.pickingImageShader.uniforms["drawing"], 7);
114044
+ this.pickingImageShader.mvpLoc = this.pickingImageShader.uniforms["mvpMtx"];
114045
+ this.pickingImageShader.rayDirLoc = this.pickingImageShader.uniforms["rayDir"];
114046
+ this.pickingImageShader.clipPlaneLoc = this.pickingImageShader.uniforms["clipPlane"];
112561
114047
  this.sliceShader = new Shader(this.gl, vertSliceShader, fragSliceShader);
112562
114048
  this.sliceShader.use(this.gl);
114049
+ this.sliceShader.drawOpacityLoc = this.sliceShader.uniforms["drawOpacity"];
114050
+ this.sliceShader.opacityLoc = this.sliceShader.uniforms["opacity"];
114051
+ this.sliceShader.backgroundMasksOverlaysLoc = this.sliceShader.uniforms["backgroundMasksOverlays"];
114052
+ this.sliceShader.axCorSagLoc = this.sliceShader.uniforms["axCorSag"];
114053
+ this.sliceShader.sliceLoc = this.sliceShader.uniforms["slice"];
114054
+ this.sliceShader.canvasWidthHeightLoc = this.sliceShader.uniforms["canvasWidthHeight"];
114055
+ this.sliceShader.leftTopWidthHeightLoc = this.sliceShader.uniforms["leftTopWidthHeight"];
112563
114056
  this.gl.uniform1i(this.sliceShader.uniforms["volume"], 0);
112564
114057
  this.gl.uniform1i(this.sliceShader.uniforms["overlay"], 2);
112565
114058
  this.gl.uniform1i(this.sliceShader.uniforms["drawing"], 7);
112566
114059
  this.gl.uniform1f(this.sliceShader.uniforms["drawOpacity"], this.drawOpacity);
112567
114060
  this.sliceMMShader = new Shader(this.gl, vertSliceMMShader, fragSliceMMShader);
112568
114061
  this.sliceMMShader.use(this.gl);
114062
+ this.sliceMMShader.drawOpacityLoc = this.sliceMMShader.uniforms["drawOpacity"];
114063
+ this.sliceMMShader.backgroundMasksOverlaysLoc = this.sliceMMShader.uniforms["backgroundMasksOverlays"];
114064
+ this.sliceMMShader.opacityLoc = this.sliceMMShader.uniforms["opacity"];
114065
+ this.sliceMMShader.axCorSagLoc = this.sliceMMShader.uniforms["axCorSag"];
114066
+ this.sliceMMShader.sliceLoc = this.sliceMMShader.uniforms["slice"];
114067
+ this.sliceMMShader.frac2mmLoc = this.sliceMMShader.uniforms["frac2mm"];
114068
+ this.sliceMMShader.mvpLoc = this.sliceMMShader.uniforms["mvpMtx"];
112569
114069
  this.gl.uniform1i(this.sliceMMShader.uniforms["volume"], 0);
112570
114070
  this.gl.uniform1i(this.sliceMMShader.uniforms["overlay"], 2);
112571
114071
  this.gl.uniform1i(this.sliceMMShader.uniforms["drawing"], 7);
112572
114072
  this.gl.uniform1f(this.sliceMMShader.uniforms["drawOpacity"], this.drawOpacity);
114073
+ this.orientCubeShader = new Shader(this.gl, vertOrientCubeShader, fragOrientCubeShader);
114074
+ let gl = this.gl;
114075
+ this.orientCubeShaderVAO = gl.createVertexArray();
114076
+ gl.bindVertexArray(this.orientCubeShaderVAO);
114077
+ let program = this.orientCubeShader.program;
114078
+ this.orientCubeMtxLoc = gl.getUniformLocation(program, "u_matrix");
114079
+ var positionBuffer = gl.createBuffer();
114080
+ gl.enableVertexAttribArray(0);
114081
+ gl.enableVertexAttribArray(1);
114082
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
114083
+ gl.bufferData(gl.ARRAY_BUFFER, orientCube, gl.STATIC_DRAW);
114084
+ gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 24, 0);
114085
+ gl.enableVertexAttribArray(1);
114086
+ gl.vertexAttribPointer(1, 3, gl.FLOAT, false, 24, 12);
114087
+ gl.bindVertexArray(this.unusedVAO);
112573
114088
  this.rectShader = new Shader(this.gl, vertRectShader, fragRectShader);
114089
+ this.rectShader.use(this.gl);
114090
+ this.rectShader.lineColorLoc = this.rectShader.uniforms["lineColor"];
114091
+ this.rectShader.canvasWidthHeightLoc = this.rectShader.uniforms["canvasWidthHeight"];
114092
+ this.rectShader.leftTopWidthHeightLoc = this.rectShader.uniforms["leftTopWidthHeight"];
112574
114093
  this.lineShader = new Shader(this.gl, vertLineShader, fragRectShader);
112575
- this.graphShader = new Shader(this.gl, vertGraphShader, fragRectShader);
114094
+ this.lineShader.use(this.gl);
114095
+ this.lineShader.lineColorLoc = this.lineShader.uniforms["lineColor"];
114096
+ this.lineShader.canvasWidthHeightLoc = this.lineShader.uniforms["canvasWidthHeight"];
114097
+ this.lineShader.thicknessLoc = this.lineShader.uniforms["thickness"];
114098
+ this.lineShader.startXYendXYLoc = this.lineShader.uniforms["startXYendXY"];
112576
114099
  this.renderShader = new Shader(this.gl, vertRenderShader, fragRenderShader);
112577
114100
  this.renderShader.use(this.gl);
114101
+ this.renderShader.drawOpacityLoc = this.renderShader.uniforms["drawOpacity"];
114102
+ this.renderShader.backgroundMasksOverlaysLoc = this.renderShader.uniforms["backgroundMasksOverlays"];
112578
114103
  this.gl.uniform1i(this.renderShader.uniforms["volume"], 0);
112579
114104
  this.gl.uniform1i(this.renderShader.uniforms["overlay"], 2);
112580
- this.renderShader.mvpUniformLoc = this.renderShader.uniforms["mvpMtx"], this.renderShader.mvpMatRASLoc = this.renderShader.uniforms["matRAS"];
112581
- this.renderShader.rayDirUniformLoc = this.renderShader.uniforms["rayDir"], this.renderShader.clipPlaneUniformLoc = this.renderShader.uniforms["clipPlane"], this.colorbarShader = new Shader(this.gl, vertColorbarShader, fragColorbarShader);
114105
+ this.gl.uniform1i(this.renderShader.uniforms["drawing"], 7);
114106
+ this.renderShader.mvpLoc = this.renderShader.uniforms["mvpMtx"];
114107
+ this.renderShader.clipPlaneClrLoc = this.renderShader.uniforms["clipPlaneColor"];
114108
+ this.renderShader.mvpMatRASLoc = this.renderShader.uniforms["matRAS"];
114109
+ this.renderShader.rayDirLoc = this.renderShader.uniforms["rayDir"];
114110
+ this.renderShader.clipPlaneLoc = this.renderShader.uniforms["clipPlane"];
114111
+ this.colorbarShader = new Shader(this.gl, vertColorbarShader, fragColorbarShader);
112582
114112
  this.colorbarShader.use(this.gl);
114113
+ this.colorbarShader.layerLoc = this.colorbarShader.uniforms["layer"];
114114
+ this.colorbarShader.canvasWidthHeightLoc = this.colorbarShader.uniforms["canvasWidthHeight"];
114115
+ this.colorbarShader.leftTopWidthHeightLoc = this.colorbarShader.uniforms["leftTopWidthHeight"];
112583
114116
  this.gl.uniform1i(this.colorbarShader.uniforms["colormap"], 1);
112584
114117
  this.growCutShader = new Shader(this.gl, vertGrowCutShader, fragGrowCutShader);
112585
114118
  this.passThroughShader = new Shader(this.gl, vertPassThroughShader, fragPassThroughShader);
@@ -112588,10 +114121,19 @@ Niivue.prototype.init = async function() {
112588
114121
  this.orientShaderI = new Shader(this.gl, vertOrientShader, fragOrientShaderI.concat(fragOrientShader));
112589
114122
  this.orientShaderF = new Shader(this.gl, vertOrientShader, fragOrientShaderF.concat(fragOrientShader));
112590
114123
  this.orientShaderRGBU = new Shader(this.gl, vertOrientShader, fragOrientShaderU.concat(fragRGBOrientShader));
112591
- this.pickingSurfaceShader = new Shader(this.gl, vertRenderShader, fragDepthPickingShader);
112592
114124
  this.surfaceShader = new Shader(this.gl, vertSurfaceShader, fragSurfaceShader);
114125
+ this.surfaceShader.use(this.gl);
114126
+ this.surfaceShader.mvpLoc = this.surfaceShader.uniforms["mvpMtx"];
114127
+ this.surfaceShader.colorLoc = this.surfaceShader.uniforms["surfaceColor"];
112593
114128
  this.fiberShader = new Shader(this.gl, vertFiberShader, fragFiberShader);
112594
- this.meshShader = new Shader(this.gl, vertMeshShader, this.meshShaders[0].Frag);
114129
+ this.pickingImageShader.use(this.gl);
114130
+ this.fiberShader.mvpLoc = this.fiberShader.uniforms["mvpMtx"];
114131
+ for (var i2 = 0; i2 < this.meshShaders.length; i2++) {
114132
+ let m = this.meshShaders[i2];
114133
+ m.shader = new Shader(this.gl, vertMeshShader, m.Frag);
114134
+ m.shader.use(this.gl);
114135
+ m.shader.mvpLoc = m.shader.uniforms["mvpMtx"];
114136
+ }
112595
114137
  this.bmpShader = new Shader(this.gl, vertBmpShader, fragBmpShader);
112596
114138
  await this.initText();
112597
114139
  if (this.opts.thumbnail.length > 0) {
@@ -112600,6 +114142,7 @@ Niivue.prototype.init = async function() {
112600
114142
  }
112601
114143
  this.updateGLVolume();
112602
114144
  this.initialized = true;
114145
+ this.resizeListener();
112603
114146
  this.drawScene();
112604
114147
  return this;
112605
114148
  };
@@ -112673,7 +114216,6 @@ Niivue.prototype.getDescriptives = function(layer = 0, ignoreZeros = false, mask
112673
114216
  M = Mnext;
112674
114217
  }
112675
114218
  let stdev = Math.sqrt(S / (k - 1));
112676
- "Number of voxels: " + k + "\nMean:" + M + "\nMin:" + mn + "\nMax:" + mx + "\nStandard deviation: " + stdev + "\nRobust Min: " + this.volumes[layer].robust_min + "\nRobust Max: " + this.volumes[layer].robust_max;
112677
114219
  return {
112678
114220
  mean: M,
112679
114221
  stdev,
@@ -112684,7 +114226,7 @@ Niivue.prototype.getDescriptives = function(layer = 0, ignoreZeros = false, mask
112684
114226
  robust_max: this.volumes[layer].robust_max
112685
114227
  };
112686
114228
  };
112687
- Niivue.prototype.refreshLayers = function(overlayItem, layer, numLayers) {
114229
+ Niivue.prototype.refreshLayers = function(overlayItem, layer) {
112688
114230
  if (this.volumes.length < 1)
112689
114231
  return;
112690
114232
  let hdr = overlayItem.hdr;
@@ -112711,8 +114253,7 @@ Niivue.prototype.refreshLayers = function(overlayItem, layer, numLayers) {
112711
114253
  this.renderShader.use(this.gl);
112712
114254
  this.gl.uniform3fv(this.renderShader.uniforms["texVox"], vox2);
112713
114255
  this.gl.uniform3fv(this.renderShader.uniforms["volScale"], volScale2);
112714
- this.renderShader;
112715
- let pickingShader = this.pickingShader;
114256
+ let pickingShader = this.pickingImageShader;
112716
114257
  pickingShader.use(this.gl);
112717
114258
  this.gl.uniform1i(pickingShader.uniforms["volume"], 0);
112718
114259
  this.gl.uniform1i(pickingShader.uniforms["colormap"], 1);
@@ -112894,14 +114435,14 @@ Niivue.prototype.refreshLayers = function(overlayItem, layer, numLayers) {
112894
114435
  let vox = slicescl.vox;
112895
114436
  let volScale = slicescl.volScale;
112896
114437
  this.gl.uniform1f(this.renderShader.uniforms["overlays"], this.overlays);
112897
- this.gl.uniform4fv(this.renderShader.uniforms["clipPlaneColor"], this.opts.clipPlaneColor);
114438
+ this.gl.uniform4fv(this.renderShader.clipPlaneClrLoc, this.opts.clipPlaneColor);
112898
114439
  this.gl.uniform1f(this.renderShader.uniforms["backOpacity"], this.volumes[0].opacity);
112899
114440
  this.gl.uniform4fv(this.renderShader.uniforms["clipPlane"], this.scene.clipPlane);
112900
114441
  this.gl.uniform3fv(this.renderShader.uniforms["texVox"], vox);
112901
114442
  this.gl.uniform3fv(this.renderShader.uniforms["volScale"], volScale);
112902
- this.pickingShader.use(this.gl);
112903
- this.gl.uniform1f(this.pickingShader.uniforms["overlays"], this.overlays.length);
112904
- this.gl.uniform3fv(this.pickingShader.uniforms["texVox"], vox);
114443
+ this.pickingImageShader.use(this.gl);
114444
+ this.gl.uniform1f(this.pickingImageShader.uniforms["overlays"], this.overlays.length);
114445
+ this.gl.uniform3fv(this.pickingImageShader.uniforms["texVox"], vox);
112905
114446
  this.sliceMMShader.use(this.gl);
112906
114447
  this.gl.uniform1f(this.sliceMMShader.uniforms["overlays"], this.overlays.length);
112907
114448
  this.gl.uniform1f(this.sliceMMShader.uniforms["drawOpacity"], this.drawOpacity);
@@ -112948,6 +114489,10 @@ Niivue.prototype.setFrame4D = function(id, frame4D) {
112948
114489
  }
112949
114490
  this.volumes[idx].frame4D = frame4D;
112950
114491
  this.updateGLVolume();
114492
+ if (this.isInSession && this.mediaUrlMap.has(this.volumes[idx])) {
114493
+ let url = this.mediaUrlMap.get(this.volumes[idx]);
114494
+ this.serverConnection$.next(new NVMessage(SET_4D_VOL_INDEX, new NVMessageSet4DVolumeIndexData(url, frame4D), this.sessionKey));
114495
+ }
112951
114496
  };
112952
114497
  Niivue.prototype.getFrame4D = function(id) {
112953
114498
  let idx = this.getVolumeIndexByID(id);
@@ -112974,12 +114519,12 @@ Niivue.prototype.refreshColormaps = function() {
112974
114519
  this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_R, this.gl.CLAMP_TO_EDGE);
112975
114520
  this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
112976
114521
  this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT, 1);
112977
- let luts2 = this.colormap(this.volumes[0].colorMap);
114522
+ let luts = this.colormap(this.volumes[0].colorMap);
112978
114523
  function addColormap(lut) {
112979
- let c2 = new Uint8ClampedArray(luts2.length + lut.length);
112980
- c2.set(luts2);
112981
- c2.set(lut, luts2.length);
112982
- luts2 = c2;
114524
+ let c2 = new Uint8ClampedArray(luts.length + lut.length);
114525
+ c2.set(luts);
114526
+ c2.set(lut, luts.length);
114527
+ luts = c2;
112983
114528
  }
112984
114529
  for (let i2 = 1; i2 < nLayer; i2++)
112985
114530
  addColormap(this.colormap(this.volumes[i2].colorMap));
@@ -112998,7 +114543,7 @@ Niivue.prototype.refreshColormaps = function() {
112998
114543
  lut = this.colormap(this.volumes[i2].colorMapNegative);
112999
114544
  addColormap(lut);
113000
114545
  }
113001
- this.gl.texSubImage2D(this.gl.TEXTURE_2D, 0, 0, 0, 256, nLayer * 2, this.gl.RGBA, this.gl.UNSIGNED_BYTE, luts2);
114546
+ this.gl.texSubImage2D(this.gl.TEXTURE_2D, 0, 0, 0, 256, nLayer * 2, this.gl.RGBA, this.gl.UNSIGNED_BYTE, luts);
113002
114547
  return this;
113003
114548
  };
113004
114549
  Niivue.prototype.sliceScale = function() {
@@ -113051,6 +114596,8 @@ Niivue.prototype.sliceScroll3D = function(posChange = 0) {
113051
114596
  this.drawScene();
113052
114597
  };
113053
114598
  Niivue.prototype.mouseClick = function(x2, y, posChange = 0, isDelta = true) {
114599
+ x2 *= this.scene.dpr;
114600
+ y *= this.scene.dpr;
113054
114601
  var posNow;
113055
114602
  var posFuture;
113056
114603
  this.canvas.focus();
@@ -113178,13 +114725,13 @@ Niivue.prototype.drawRuler = function() {
113178
114725
  Niivue.prototype.drawRuler10cm = function(startXYendXY) {
113179
114726
  this.gl.bindVertexArray(this.genericVAO);
113180
114727
  this.lineShader.use(this.gl);
113181
- this.gl.uniform4fv(this.lineShader.uniforms["lineColor"], this.opts.rulerColor);
113182
- this.gl.uniform2fv(this.lineShader.uniforms["canvasWidthHeight"], [
114728
+ this.gl.uniform4fv(this.lineShader.lineColorLoc, this.opts.rulerColor);
114729
+ this.gl.uniform2fv(this.lineShader.canvasWidthHeightLoc, [
113183
114730
  this.gl.canvas.width,
113184
114731
  this.gl.canvas.height
113185
114732
  ]);
113186
- this.gl.uniform1f(this.lineShader.uniforms["thickness"], this.opts.rulerWidth);
113187
- this.gl.uniform4fv(this.lineShader.uniforms["startXYendXY"], startXYendXY);
114733
+ this.gl.uniform1f(this.lineShader.thicknessLoc, this.opts.rulerWidth);
114734
+ this.gl.uniform4fv(this.lineShader.startXYendXYLoc, startXYendXY);
113188
114735
  this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
113189
114736
  let w1cm = -0.1 * (startXYendXY[0] - startXYendXY[2]);
113190
114737
  let b = startXYendXY[1];
@@ -113195,11 +114742,45 @@ Niivue.prototype.drawRuler10cm = function(startXYendXY) {
113195
114742
  let xyxy = [l, b, l, t];
113196
114743
  if (i2 % 5 === 0)
113197
114744
  xyxy[3] = t2;
113198
- this.gl.uniform4fv(this.lineShader.uniforms["startXYendXY"], xyxy);
114745
+ this.gl.uniform4fv(this.lineShader.startXYendXYLoc, xyxy);
113199
114746
  this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
113200
114747
  }
113201
114748
  this.gl.bindVertexArray(this.unusedVAO);
113202
114749
  };
114750
+ Niivue.prototype.screenXY2mm = function(x2, y, forceSlice = -1) {
114751
+ for (let s = 0; s < this.screenSlices.length; s++) {
114752
+ let i2 = s;
114753
+ if (forceSlice >= 0)
114754
+ i2 = forceSlice;
114755
+ var axCorSag = this.screenSlices[i2].axCorSag;
114756
+ if (axCorSag > this.sliceTypeSagittal)
114757
+ continue;
114758
+ let ltwh = this.screenSlices[i2].leftTopWidthHeight;
114759
+ if (x2 < ltwh[0] || y < ltwh[1] || x2 > ltwh[0] + ltwh[2] || y > ltwh[1] + ltwh[3])
114760
+ continue;
114761
+ console.log(this.screenSlices[i2].leftTopWidthHeight, x2, y);
114762
+ let texFrac = this.screenXY2TextureFrac(x2, y, i2, false);
114763
+ if (forceSlice < 0 && texFrac[0] < 0)
114764
+ continue;
114765
+ let mm = this.frac2mm(texFrac);
114766
+ return [mm[0], mm[1], mm[2], i2];
114767
+ }
114768
+ return [NaN, NaN, NaN, NaN];
114769
+ };
114770
+ Niivue.prototype.dragForPanZoom = function(startXYendXY) {
114771
+ let endMM = this.screenXY2mm(startXYendXY[2], startXYendXY[3]);
114772
+ if (isNaN(endMM[0]))
114773
+ return;
114774
+ let startMM = this.screenXY2mm(startXYendXY[0], startXYendXY[1], endMM[3]);
114775
+ if (isNaN(startMM[0]) || isNaN(endMM[0]) || endMM[3] !== endMM[3]) {
114776
+ return;
114777
+ }
114778
+ let v = create$1();
114779
+ sub(v, endMM, startMM);
114780
+ this.scene.pan2Dxyzmm[0] = this.scene.pan2DxyzmmAtMouseDown[0] + v[0];
114781
+ this.scene.pan2Dxyzmm[1] = this.scene.pan2DxyzmmAtMouseDown[1] + v[1];
114782
+ this.scene.pan2Dxyzmm[2] = this.scene.pan2DxyzmmAtMouseDown[2] + v[2];
114783
+ };
113203
114784
  Niivue.prototype.drawMeasurementTool = function(startXYendXY) {
113204
114785
  let gl = this.gl;
113205
114786
  gl.bindVertexArray(this.genericVAO);
@@ -113207,26 +114788,26 @@ Niivue.prototype.drawMeasurementTool = function(startXYendXY) {
113207
114788
  gl.enable(gl.BLEND);
113208
114789
  gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
113209
114790
  this.lineShader.use(this.gl);
113210
- gl.uniform4fv(this.lineShader.uniforms["lineColor"], this.opts.rulerColor);
113211
- gl.uniform2fv(this.lineShader.uniforms["canvasWidthHeight"], [
114791
+ gl.uniform4fv(this.lineShader.lineColorLoc, this.opts.rulerColor);
114792
+ gl.uniform2fv(this.lineShader.canvasWidthHeightLoc, [
113212
114793
  gl.canvas.width,
113213
114794
  gl.canvas.height
113214
114795
  ]);
113215
- gl.uniform1f(this.lineShader.uniforms["thickness"], this.opts.rulerWidth);
113216
- gl.uniform4fv(this.lineShader.uniforms["startXYendXY"], startXYendXY);
114796
+ gl.uniform1f(this.lineShader.thicknessLoc, this.opts.rulerWidth);
114797
+ gl.uniform4fv(this.lineShader.startXYendXYLoc, startXYendXY);
113217
114798
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
113218
114799
  let c2 = this.opts.rulerColor;
113219
114800
  c2[3] = 1;
113220
- gl.uniform4fv(this.lineShader.uniforms["lineColor"], c2);
114801
+ gl.uniform4fv(this.lineShader.lineColorLoc, c2);
113221
114802
  let w = this.opts.rulerWidth;
113222
- gl.uniform1f(this.lineShader.uniforms["thickness"], w * 2);
114803
+ gl.uniform1f(this.lineShader.thicknessLoc, w * 2);
113223
114804
  let sXYeXY = [
113224
114805
  startXYendXY[0],
113225
114806
  startXYendXY[1] - w,
113226
114807
  startXYendXY[0],
113227
114808
  startXYendXY[1] + w
113228
114809
  ];
113229
- gl.uniform4fv(this.lineShader.uniforms["startXYendXY"], sXYeXY);
114810
+ gl.uniform4fv(this.lineShader.startXYendXYLoc, sXYeXY);
113230
114811
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
113231
114812
  sXYeXY = [
113232
114813
  startXYendXY[2],
@@ -113234,7 +114815,7 @@ Niivue.prototype.drawMeasurementTool = function(startXYendXY) {
113234
114815
  startXYendXY[2],
113235
114816
  startXYendXY[3] + w
113236
114817
  ];
113237
- gl.uniform4fv(this.lineShader.uniforms["startXYendXY"], sXYeXY);
114818
+ gl.uniform4fv(this.lineShader.startXYendXYLoc, sXYeXY);
113238
114819
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
113239
114820
  let startXY = this.canvasPos2frac([startXYendXY[0], startXYendXY[1]]);
113240
114821
  let endXY = this.canvasPos2frac([startXYendXY[2], startXYendXY[3]]);
@@ -113254,21 +114835,23 @@ Niivue.prototype.drawMeasurementTool = function(startXYendXY) {
113254
114835
  }
113255
114836
  gl.bindVertexArray(this.unusedVAO);
113256
114837
  };
113257
- Niivue.prototype.drawBox = function(leftTopWidthHeight, lineColor = [1, 0, 0, 1]) {
114838
+ Niivue.prototype.drawRect = function(leftTopWidthHeight, lineColor = [1, 0, 0, -1]) {
114839
+ if (lineColor[3] < 0)
114840
+ lineColor = this.opts.crosshairColor;
113258
114841
  this.rectShader.use(this.gl);
113259
114842
  this.gl.enable(this.gl.BLEND);
113260
- this.gl.uniform4fv(this.rectShader.uniforms["lineColor"], lineColor);
113261
- this.gl.uniform2fv(this.rectShader.uniforms["canvasWidthHeight"], [
114843
+ this.gl.uniform4fv(this.rectShader.lineColorLoc, lineColor);
114844
+ this.gl.uniform2fv(this.rectShader.canvasWidthHeightLoc, [
113262
114845
  this.gl.canvas.width,
113263
114846
  this.gl.canvas.height
113264
114847
  ]);
113265
- this.gl.uniform4f(this.rectShader.uniforms["leftTopWidthHeight"], leftTopWidthHeight[0], leftTopWidthHeight[1], leftTopWidthHeight[2], leftTopWidthHeight[3]);
114848
+ this.gl.uniform4f(this.rectShader.leftTopWidthHeightLoc, leftTopWidthHeight[0], leftTopWidthHeight[1], leftTopWidthHeight[2], leftTopWidthHeight[3]);
113266
114849
  this.gl.bindVertexArray(this.genericVAO);
113267
114850
  this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
113268
114851
  this.gl.bindVertexArray(this.unusedVAO);
113269
114852
  };
113270
- Niivue.prototype.drawSelectionBox = function(leftTopWidthHeight, lineColor = [1, 0, 0, 1]) {
113271
- this.drawBox(leftTopWidthHeight, this.opts.selectionBoxColor);
114853
+ Niivue.prototype.drawSelectionBox = function(leftTopWidthHeight) {
114854
+ this.drawRect(leftTopWidthHeight, this.opts.selectionBoxColor);
113272
114855
  };
113273
114856
  function tickSpacingX(tickCount, mn, mx) {
113274
114857
  let range = Math.abs(mx - mn);
@@ -113285,13 +114868,22 @@ function tickSpacingX(tickCount, mn, mx) {
113285
114868
  ticMin = Math.sign(ticMin) * Math.round(Math.abs(ticMin));
113286
114869
  return [spacing, ticMin];
113287
114870
  }
114871
+ function isDivisible(num, denom) {
114872
+ return Math.abs(num - Math.round(num / denom) * (denom / num)) < 1e-5;
114873
+ }
113288
114874
  function tickSpacing(mn, mx) {
113289
114875
  let range = Math.abs(mx - mn);
113290
114876
  let [spacing, ticMin] = tickSpacingX(5, mn, mx);
113291
- if (range % spacing === 0)
114877
+ if (isDivisible(range, spacing))
113292
114878
  return [spacing, ticMin];
113293
114879
  [spacing, ticMin] = tickSpacingX(4, mn, mx);
113294
- if (range % spacing === 0)
114880
+ if (isDivisible(range, spacing))
114881
+ return [spacing, ticMin];
114882
+ [spacing, ticMin] = tickSpacingX(6, mn, mx);
114883
+ if (isDivisible(range, spacing))
114884
+ return [spacing, ticMin];
114885
+ [spacing, ticMin] = tickSpacingX(7, mn, mx);
114886
+ if (isDivisible(range, spacing))
113295
114887
  return [spacing, ticMin];
113296
114888
  [spacing, ticMin] = tickSpacingX(5, mn, mx);
113297
114889
  return [spacing, ticMin];
@@ -113332,13 +114924,6 @@ Niivue.prototype.drawColorbarCore = function(layer = 0, leftTopWidthHeight = [0,
113332
114924
  }
113333
114925
  this.gl.disable(this.gl.DEPTH_TEST);
113334
114926
  this.colorbarHeight = leftTopWidthHeight[3] + 1;
113335
- [
113336
- this.opts.backColor[0],
113337
- this.opts.backColor[1],
113338
- this.opts.backColor[2],
113339
- 0.5
113340
- ];
113341
- leftTopWidthHeight[1];
113342
114927
  let barLTWH = [
113343
114928
  leftTopWidthHeight[0] + margin,
113344
114929
  leftTopWidthHeight[1],
@@ -113351,7 +114936,7 @@ Niivue.prototype.drawColorbarCore = function(layer = 0, leftTopWidthHeight = [0,
113351
114936
  barLTWH[2] + 2,
113352
114937
  barLTWH[3] + 2
113353
114938
  ];
113354
- this.drawBox(rimLTWH, this.opts.crosshairColor);
114939
+ this.drawRect(rimLTWH, this.opts.crosshairColor);
113355
114940
  this.colorbarShader.use(this.gl);
113356
114941
  this.gl.activeTexture(this.gl.TEXTURE1);
113357
114942
  this.gl.bindTexture(this.gl.TEXTURE_2D, this.colormapTexture);
@@ -113360,12 +114945,12 @@ Niivue.prototype.drawColorbarCore = function(layer = 0, leftTopWidthHeight = [0,
113360
114945
  let lx = layer;
113361
114946
  if (isNegativeColor)
113362
114947
  lx += this.volumes.length;
113363
- this.gl.uniform1f(this.colorbarShader.uniforms["layer"], lx);
113364
- this.gl.uniform2fv(this.colorbarShader.uniforms["canvasWidthHeight"], [
114948
+ this.gl.uniform1f(this.colorbarShader.layerLoc, lx);
114949
+ this.gl.uniform2fv(this.colorbarShader.canvasWidthHeightLoc, [
113365
114950
  this.gl.canvas.width,
113366
114951
  this.gl.canvas.height
113367
114952
  ]);
113368
- this.gl.uniform4fv(this.colorbarShader.uniforms["leftTopWidthHeight"], barLTWH);
114953
+ this.gl.uniform4fv(this.colorbarShader.leftTopWidthHeightLoc, barLTWH);
113369
114954
  this.gl.bindVertexArray(this.genericVAO);
113370
114955
  this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
113371
114956
  this.gl.bindVertexArray(this.unusedVAO);
@@ -113385,7 +114970,7 @@ Niivue.prototype.drawColorbarCore = function(layer = 0, leftTopWidthHeight = [0,
113385
114970
  let txtTop = ticLTWH[1] + ticLTWH[3];
113386
114971
  while (tic <= max2) {
113387
114972
  ticLTWH[0] = barLTWH[0] + (tic - min2) / range * barLTWH[2];
113388
- this.drawBox(ticLTWH);
114973
+ this.drawRect(ticLTWH);
113389
114974
  let str = humanize(tic);
113390
114975
  if (isNegativeColor)
113391
114976
  str = "-" + str;
@@ -113434,8 +115019,8 @@ Niivue.prototype.drawChar = function(xy, scale2, char) {
113434
115019
  let w = scale2 * metrics2.lbwh[2];
113435
115020
  let h = scale2 * metrics2.lbwh[3];
113436
115021
  let t = xy[1] + (b - h) + scale2;
113437
- this.gl.uniform4f(this.fontShader.uniforms["leftTopWidthHeight"], l, t, w, h);
113438
- this.gl.uniform4fv(this.fontShader.uniforms["uvLeftTopWidthHeight"], metrics2.uv_lbwh);
115022
+ this.gl.uniform4f(this.fontShader.leftTopWidthHeightLoc, l, t, w, h);
115023
+ this.gl.uniform4fv(this.fontShader.uvLeftTopWidthHeightLoc, metrics2.uv_lbwh);
113439
115024
  this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
113440
115025
  return scale2 * metrics2.xadv;
113441
115026
  };
@@ -113452,13 +115037,13 @@ Niivue.prototype.drawText = function(xy, str, scale2 = 1, color = null) {
113452
115037
  this.fontShader.use(this.gl);
113453
115038
  let size = this.opts.textHeight * Math.min(this.gl.canvas.height, this.gl.canvas.width) * scale2;
113454
115039
  this.gl.enable(this.gl.BLEND);
113455
- this.gl.uniform2f(this.fontShader.uniforms["canvasWidthHeight"], this.gl.canvas.width, this.gl.canvas.height);
115040
+ this.gl.uniform2f(this.fontShader.canvasWidthHeightLoc, this.gl.canvas.width, this.gl.canvas.height);
113456
115041
  if (color === null)
113457
115042
  color = this.opts.crosshairColor;
113458
- this.gl.uniform4fv(this.fontShader.uniforms["fontColor"], color);
115043
+ this.gl.uniform4fv(this.fontShader.fontColorLoc, color);
113459
115044
  let screenPxRange = size / this.fontMets.size * this.fontMets.distanceRange;
113460
115045
  screenPxRange = Math.max(screenPxRange, 1);
113461
- this.gl.uniform1f(this.fontShader.uniforms["screenPxRange"], screenPxRange);
115046
+ this.gl.uniform1f(this.fontShader.screenPxRangeLoc, screenPxRange);
113462
115047
  var bytes = new TextEncoder().encode(str);
113463
115048
  this.gl.bindVertexArray(this.genericVAO);
113464
115049
  for (let i2 = 0; i2 < str.length; i2++)
@@ -113482,7 +115067,6 @@ Niivue.prototype.drawTextLeft = function(xy, str, scale2 = 1, color = null) {
113482
115067
  Niivue.prototype.drawTextRightBelow = function(xy, str, scale2 = 1, color = null) {
113483
115068
  if (this.opts.textHeight <= 0)
113484
115069
  return;
113485
- this.opts.textHeight * this.gl.canvas.height * scale2;
113486
115070
  this.drawText(xy, str, scale2, color);
113487
115071
  };
113488
115072
  Niivue.prototype.drawTextBetween = function(startXYendXY, str, scale2 = 1, color = null) {
@@ -113545,7 +115129,7 @@ function deg2rad(deg) {
113545
115129
  }
113546
115130
  Niivue.prototype.calculateMvpMatrix2D = function(leftTopWidthHeight, mn, mx, clipTolerance = Infinity, clipDepth = 0, azimuth = null, elevation = null, isRadiolgical) {
113547
115131
  let gl = this.gl;
113548
- gl.viewport(leftTopWidthHeight[0], this.gl.canvas.clientHeight - (leftTopWidthHeight[1] + leftTopWidthHeight[3]), leftTopWidthHeight[2], leftTopWidthHeight[3]);
115132
+ gl.viewport(leftTopWidthHeight[0], this.gl.canvas.height - (leftTopWidthHeight[1] + leftTopWidthHeight[3]), leftTopWidthHeight[2], leftTopWidthHeight[3]);
113549
115133
  let left = mn[0];
113550
115134
  let right = mx[0];
113551
115135
  let leftTopMM = [left, mn[1]];
@@ -113606,7 +115190,6 @@ Niivue.prototype.screenFieldOfViewMM = function(axCorSag = 0, forceSliceMM = fal
113606
115190
  }
113607
115191
  let mnMM = this.volumeObject3D.extentsMin.slice();
113608
115192
  let mxMM = this.volumeObject3D.extentsMax.slice();
113609
- create$2();
113610
115193
  mnMM = this.swizzleVec3MM(mnMM, axCorSag);
113611
115194
  mxMM = this.swizzleVec3MM(mxMM, axCorSag);
113612
115195
  let fovMM = create$1();
@@ -113734,15 +115317,16 @@ Niivue.prototype.draw2DMM = function(leftTopWidthHeight, axCorSag, customMM = Na
113734
115317
  gl.depthFunc(gl.GREATER);
113735
115318
  gl.disable(gl.CULL_FACE);
113736
115319
  this.sliceMMShader.use(this.gl);
113737
- this.gl.uniform1i(this.sliceMMShader.uniforms["backgroundMasksOverlays"], this.backgroundMasksOverlays);
113738
- this.gl.uniform1f(this.sliceMMShader.uniforms["opacity"], this.volumes[0].opacity);
113739
- this.gl.uniform1i(this.sliceMMShader.uniforms["axCorSag"], axCorSag);
113740
- this.gl.uniform1f(this.sliceMMShader.uniforms["slice"], sliceFrac);
113741
- this.gl.uniformMatrix4fv(this.sliceMMShader.uniforms["frac2mm"], false, this.volumes[0].frac2mm);
113742
- this.gl.uniformMatrix4fv(this.sliceMMShader.uniforms["mvpMtx"], false, obj.modelViewProjectionMatrix.slice());
113743
- this.gl.bindVertexArray(this.genericVAO);
113744
- this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
113745
- this.gl.bindVertexArray(this.unusedVAO);
115320
+ gl.uniform1i(this.sliceMMShader.backgroundMasksOverlaysLoc, this.backgroundMasksOverlays);
115321
+ gl.uniform1f(this.sliceMMShader.drawOpacityLoc, this.drawOpacity);
115322
+ gl.uniform1f(this.sliceMMShader.opacityLoc, this.volumes[0].opacity);
115323
+ gl.uniform1i(this.sliceMMShader.axCorSagLoc, axCorSag);
115324
+ gl.uniform1f(this.sliceMMShader.sliceLoc, sliceFrac);
115325
+ gl.uniformMatrix4fv(this.sliceMMShader.frac2mmLoc, false, this.volumes[0].frac2mm);
115326
+ gl.uniformMatrix4fv(this.sliceMMShader.mvpLoc, false, obj.modelViewProjectionMatrix.slice());
115327
+ gl.bindVertexArray(this.genericVAO);
115328
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
115329
+ gl.bindVertexArray(this.unusedVAO);
113746
115330
  this.screenSlices.push({
113747
115331
  leftTopWidthHeight,
113748
115332
  axCorSag,
@@ -113752,7 +115336,7 @@ Niivue.prototype.draw2DMM = function(leftTopWidthHeight, axCorSag, customMM = Na
113752
115336
  fovMM: obj.fovMM
113753
115337
  });
113754
115338
  if (isNaN(customMM)) {
113755
- this.drawCrosshairs3D(true, 1, obj.modelViewProjectionMatrix);
115339
+ this.drawCrosshairs3D(true, 1, obj.modelViewProjectionMatrix, true);
113756
115340
  }
113757
115341
  if (this.opts.meshThicknessOn2D > 0) {
113758
115342
  if (this.opts.meshThicknessOn2D !== Infinity)
@@ -113760,18 +115344,19 @@ Niivue.prototype.draw2DMM = function(leftTopWidthHeight, axCorSag, customMM = Na
113760
115344
  this.drawMesh3D(true, 1, obj.modelViewProjectionMatrix, obj.modelMatrix, obj.normalMatrix);
113761
115345
  }
113762
115346
  if (isNaN(customMM))
113763
- this.drawCrosshairs3D(false, 0.15, obj.modelViewProjectionMatrix);
115347
+ this.drawCrosshairs3D(false, 0.15, obj.modelViewProjectionMatrix, true);
113764
115348
  this.drawSliceOrientationText(leftTopWidthHeight, axCorSag);
113765
115349
  this.readyForSync = true;
113766
115350
  };
115351
+ Niivue.prototype.drawCro;
113767
115352
  Niivue.prototype.draw2DVox = function(leftTopWidthHeight, axCorSag, customMM = NaN) {
115353
+ let gl = this.gl;
113768
115354
  let fovMM = this.screenFieldOfViewMM(axCorSag);
113769
115355
  if (leftTopWidthHeight[2] === 0 || leftTopWidthHeight[3] === 0) {
113770
115356
  leftTopWidthHeight = this.scaleSlice(fovMM[0], fovMM[1]);
113771
115357
  }
113772
- this.gl.cullFace(this.gl.FRONT);
115358
+ gl.cullFace(gl.FRONT);
113773
115359
  let ltwh = leftTopWidthHeight.slice();
113774
- let gl = this.gl;
113775
115360
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
113776
115361
  gl.disable(gl.DEPTH_TEST);
113777
115362
  let sliceDim = 2;
@@ -113806,22 +115391,23 @@ Niivue.prototype.draw2DVox = function(leftTopWidthHeight, axCorSag, customMM = N
113806
115391
  if (this.opts.sagittalNoseLeft && axCorSag === this.sliceTypeSagittal)
113807
115392
  isMirrorLR = !isMirrorLR;
113808
115393
  this.sliceShader.use(this.gl);
113809
- this.gl.uniform1f(this.sliceShader.uniforms["opacity"], this.volumes[0].opacity);
113810
- this.gl.uniform1i(this.sliceShader.uniforms["backgroundMasksOverlays"], this.backgroundMasksOverlays);
113811
- this.gl.uniform1i(this.sliceShader.uniforms["axCorSag"], axCorSag);
113812
- this.gl.uniform1f(this.sliceShader.uniforms["slice"], crossXYZ[2]);
113813
- this.gl.uniform2fv(this.sliceShader.uniforms["canvasWidthHeight"], [
113814
- this.gl.canvas.width,
113815
- this.gl.canvas.height
115394
+ gl.uniform1f(this.sliceShader.drawOpacityLoc, this.drawOpacity);
115395
+ gl.uniform1f(this.sliceShader.opacityLoc, this.volumes[0].opacity);
115396
+ gl.uniform1i(this.sliceShader.backgroundMasksOverlaysLoc, this.backgroundMasksOverlays);
115397
+ gl.uniform1i(this.sliceShader.axCorSagLoc, axCorSag);
115398
+ gl.uniform1f(this.sliceShader.sliceLoc, crossXYZ[2]);
115399
+ gl.uniform2fv(this.sliceShader.canvasWidthHeightLoc, [
115400
+ gl.canvas.width,
115401
+ gl.canvas.height
113816
115402
  ]);
113817
115403
  if (isMirrorLR) {
113818
- this.gl.disable(this.gl.CULL_FACE);
115404
+ gl.disable(gl.CULL_FACE);
113819
115405
  leftTopWidthHeight[2] = -leftTopWidthHeight[2];
113820
115406
  leftTopWidthHeight[0] = leftTopWidthHeight[0] - leftTopWidthHeight[2];
113821
115407
  }
113822
- this.gl.uniform4f(this.sliceShader.uniforms["leftTopWidthHeight"], leftTopWidthHeight[0], leftTopWidthHeight[1], leftTopWidthHeight[2], leftTopWidthHeight[3]);
113823
- this.gl.bindVertexArray(this.genericVAO);
113824
- this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
115408
+ gl.uniform4f(this.sliceShader.leftTopWidthHeightLoc, leftTopWidthHeight[0], leftTopWidthHeight[1], leftTopWidthHeight[2], leftTopWidthHeight[3]);
115409
+ gl.bindVertexArray(this.genericVAO);
115410
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
113825
115411
  this.screenSlices.push({
113826
115412
  leftTopWidthHeight,
113827
115413
  axCorSag,
@@ -113836,21 +115422,21 @@ Niivue.prototype.draw2DVox = function(leftTopWidthHeight, axCorSag, customMM = N
113836
115422
  return;
113837
115423
  if (this.opts.crosshairWidth <= 0)
113838
115424
  return;
113839
- this.gl.bindVertexArray(this.genericVAO);
115425
+ gl.bindVertexArray(this.genericVAO);
113840
115426
  this.rectShader.use(this.gl);
113841
- this.gl.uniform4fv(this.rectShader.uniforms["lineColor"], this.opts.crosshairColor);
113842
- this.gl.uniform2fv(this.rectShader.uniforms["canvasWidthHeight"], [
113843
- this.gl.canvas.width,
113844
- this.gl.canvas.height
115427
+ gl.uniform4fv(this.rectShader.lineColorLoc, this.opts.crosshairColor);
115428
+ gl.uniform2fv(this.rectShader.canvasWidthHeightLoc, [
115429
+ gl.canvas.width,
115430
+ gl.canvas.height
113845
115431
  ]);
113846
115432
  var xleft = leftTopWidthHeight[0] + leftTopWidthHeight[2] * crossXYZ[0];
113847
- this.gl.uniform4f(this.rectShader.uniforms["leftTopWidthHeight"], xleft - 0.5 * this.opts.crosshairWidth, leftTopWidthHeight[1], this.opts.crosshairWidth, leftTopWidthHeight[3]);
113848
- this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
115433
+ gl.uniform4f(this.rectShader.leftTopWidthHeightLoc, xleft - 0.5 * this.opts.crosshairWidth, leftTopWidthHeight[1], this.opts.crosshairWidth, leftTopWidthHeight[3]);
115434
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
113849
115435
  var xtop = leftTopWidthHeight[1] + leftTopWidthHeight[3] * (1 - crossXYZ[1]);
113850
- this.gl.uniform4f(this.rectShader.uniforms["leftTopWidthHeight"], leftTopWidthHeight[0], xtop - 0.5 * this.opts.crosshairWidth, leftTopWidthHeight[2], this.opts.crosshairWidth);
113851
- this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
113852
- this.gl.bindVertexArray(this.unusedVAO);
113853
- this.gl.enable(this.gl.CULL_FACE);
115436
+ gl.uniform4f(this.rectShader.leftTopWidthHeightLoc, leftTopWidthHeight[0], xtop - 0.5 * this.opts.crosshairWidth, leftTopWidthHeight[2], this.opts.crosshairWidth);
115437
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
115438
+ gl.bindVertexArray(this.unusedVAO);
115439
+ gl.enable(gl.CULL_FACE);
113854
115440
  gl.enable(gl.DEPTH_TEST);
113855
115441
  gl.depthFunc(gl.ALWAYS);
113856
115442
  gl.clearDepth(0);
@@ -113896,13 +115482,10 @@ Niivue.prototype.calculateMvpMatrix = function(unused, leftTopWidthHeight = [0,
113896
115482
  return [modelViewProjectionMatrix, modelMatrix, normalMatrix];
113897
115483
  };
113898
115484
  Niivue.prototype.calculateRayDirection = function(azimuth, elevation) {
113899
- function deg2rad2(deg) {
113900
- return deg * (Math.PI / 180);
113901
- }
113902
115485
  const modelMatrix = create$2();
113903
115486
  modelMatrix[0] = -1;
113904
- rotateX(modelMatrix, modelMatrix, deg2rad2(270 - elevation));
113905
- rotateZ(modelMatrix, modelMatrix, deg2rad2(azimuth - 180));
115487
+ rotateX(modelMatrix, modelMatrix, deg2rad(270 - elevation));
115488
+ rotateZ(modelMatrix, modelMatrix, deg2rad(azimuth - 180));
113906
115489
  if (this.back.obliqueRAS) {
113907
115490
  let oblique = clone$1(this.back.obliqueRAS);
113908
115491
  multiply(modelMatrix, modelMatrix, oblique);
@@ -113925,7 +115508,7 @@ Niivue.prototype.calculateRayDirection = function(azimuth, elevation) {
113925
115508
  rayDir[2] = tiny;
113926
115509
  return rayDir;
113927
115510
  };
113928
- Niivue.prototype.setPivot3D = function() {
115511
+ Niivue.prototype.sceneExtentsMinMax = function() {
113929
115512
  let mn = fromValues$1(0, 0, 0);
113930
115513
  let mx = fromValues$1(0, 0, 0);
113931
115514
  if (this.volumes.length > 0) {
@@ -113943,6 +115526,12 @@ Niivue.prototype.setPivot3D = function() {
113943
115526
  max$T(mx, mx, v);
113944
115527
  }
113945
115528
  }
115529
+ let range = create$1();
115530
+ subtract(range, mx, mn);
115531
+ return [mn, mx, range];
115532
+ };
115533
+ Niivue.prototype.setPivot3D = function() {
115534
+ let [mn, mx] = this.sceneExtentsMinMax();
113946
115535
  let pivot = create$1();
113947
115536
  add$1(pivot, mn, mx);
113948
115537
  scale(pivot, pivot, 0.5);
@@ -113952,33 +115541,7 @@ Niivue.prototype.setPivot3D = function() {
113952
115541
  this.extentsMax = mx;
113953
115542
  this.furthestFromPivot = length$1(pivot) * 0.5;
113954
115543
  };
113955
- Niivue.prototype.drawGraphLine = function(LTRB, color = [1, 0, 0, 0.5], thickness = 2) {
113956
- this.graphShader.use(this.gl);
113957
- this.gl.uniform4fv(this.graphShader.uniforms["lineColor"], color);
113958
- this.gl.uniform2fv(this.graphShader.uniforms["canvasWidthHeight"], [
113959
- this.gl.canvas.width,
113960
- this.gl.canvas.height
113961
- ]);
113962
- this.gl.uniform1f(this.graphShader.uniforms["thickness"], thickness);
113963
- this.gl.uniform4f(this.graphShader.uniforms["leftTopRightBottom"], LTRB[0], LTRB[1], LTRB[2], LTRB[3]);
113964
- this.gl.bindVertexArray(this.genericVAO);
113965
- this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
113966
- this.gl.bindVertexArray(this.unusedVAO);
113967
- };
113968
- Niivue.prototype.drawRect = function(LTWH, color = [1, 0, 0, 0.5]) {
113969
- this.rectShader.use(this.gl);
113970
- this.gl.uniform4fv(this.rectShader.uniforms["lineColor"], color);
113971
- this.gl.uniform2fv(this.rectShader.uniforms["canvasWidthHeight"], [
113972
- this.gl.canvas.width,
113973
- this.gl.canvas.height
113974
- ]);
113975
- this.gl.uniform4f(this.rectShader.uniforms["leftTopWidthHeight"], LTWH[0], LTWH[1], LTWH[2], LTWH[3]);
113976
- this.gl.bindVertexArray(this.genericVAO);
113977
- this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
113978
- this.gl.bindVertexArray(this.unusedVAO);
113979
- };
113980
115544
  Niivue.prototype.drawGraph = function() {
113981
- this.gl;
113982
115545
  let graph = this.graph;
113983
115546
  if (this.graph.autoSizeMultiplanar && this.sliceType === this.sliceTypeMultiplanar) {
113984
115547
  for (let i2 = 0; i2 < this.screenSlices.length; i2++) {
@@ -114116,13 +115679,13 @@ Niivue.prototype.drawGraph = function() {
114116
115679
  thinColor[3] = 0.25 * graph.lineColor[3];
114117
115680
  while (lineH <= mx) {
114118
115681
  let y = plotBottom - (lineH - mn) * scaleH;
114119
- this.drawGraphLine([plotLTWH[0], y, plotLTWH[0] + plotLTWH[2], y], thinColor, 0.5 * graph.lineThickness);
115682
+ this.drawLine([plotLTWH[0], y, plotLTWH[0] + plotLTWH[2], y], 0.5 * graph.lineThickness, thinColor);
114120
115683
  lineH += spacing;
114121
115684
  }
114122
115685
  lineH = ticMin;
114123
115686
  while (lineH <= mx) {
114124
115687
  let y = plotBottom - (lineH - mn) * scaleH;
114125
- this.drawGraphLine([plotLTWH[0], y, plotLTWH[0] + plotLTWH[2], y], graph.lineColor, graph.lineThickness);
115688
+ this.drawLine([plotLTWH[0], y, plotLTWH[0] + plotLTWH[2], y], graph.lineThickness, graph.lineColor);
114126
115689
  let str = humanize(lineH);
114127
115690
  if (fntSize > 0)
114128
115691
  this.drawTextLeft([plotLTWH[0] - 6, y], str, fntScale, graph.textColor);
@@ -114137,12 +115700,12 @@ Niivue.prototype.drawGraph = function() {
114137
115700
  let thick = graph.lineThickness;
114138
115701
  if (i2 % 2 === 1) {
114139
115702
  thick *= 0.5;
114140
- this.drawGraphLine([x2, plotLTWH[1], x2, plotLTWH[1] + plotLTWH[3]], thinColor, thick);
114141
- } else if (i2 > 0) {
115703
+ this.drawLine([x2, plotLTWH[1], x2, plotLTWH[1] + plotLTWH[3]], thick, thinColor);
115704
+ } else {
114142
115705
  let str = humanize(i2);
114143
115706
  if (fntSize > 0)
114144
115707
  this.drawTextBelow([x2, 2 + plotLTWH[1] + plotLTWH[3]], str, fntScale, graph.textColor);
114145
- this.drawGraphLine([x2, plotLTWH[1], x2, plotLTWH[1] + plotLTWH[3]], graph.lineColor, thick);
115708
+ this.drawLine([x2, plotLTWH[1], x2, plotLTWH[1] + plotLTWH[3]], thick, graph.lineColor);
114146
115709
  }
114147
115710
  }
114148
115711
  for (let j = 0; j < graph.lines.length; j++) {
@@ -114166,13 +115729,12 @@ Niivue.prototype.drawGraph = function() {
114166
115729
  plotLTWH[0] + x1,
114167
115730
  plotLTWH[1] + plotLTWH[3] - y1
114168
115731
  ];
114169
- this.drawGraphLine(LTWH, lineRGBA, graph.lineThickness);
115732
+ this.drawLine(LTWH, graph.lineThickness, lineRGBA);
114170
115733
  }
114171
115734
  }
114172
115735
  if (graph.selectedColumn >= 0 && graph.selectedColumn < graph.lines[0].length) {
114173
115736
  let x2 = graph.selectedColumn * scaleW + plotLTWH[0];
114174
- graph.lineRGB[0];
114175
- this.drawGraphLine([x2, plotLTWH[1], x2, plotLTWH[1] + plotLTWH[3]], [graph.lineRGB[3][0], graph.lineRGB[3][1], graph.lineRGB[3][2], 1], graph.lineThickness);
115737
+ this.drawLine([x2, plotLTWH[1], x2, plotLTWH[1] + plotLTWH[3]], graph.lineThickness, [graph.lineRGB[3][0], graph.lineRGB[3][1], graph.lineRGB[3][2], 1]);
114176
115738
  }
114177
115739
  };
114178
115740
  function isRadiological(mtx) {
@@ -114181,6 +115743,117 @@ function isRadiological(mtx) {
114181
115743
  transformMat4(vRotated, vRight, mtx);
114182
115744
  return vRotated[0];
114183
115745
  }
115746
+ function unProject(winX, winY, winZ, mvpMatrix) {
115747
+ let inp = fromValues(winX, winY, winZ, 1);
115748
+ let finalMatrix = clone$1(mvpMatrix);
115749
+ invert(finalMatrix, finalMatrix);
115750
+ inp[0] = inp[0] * 2 - 1;
115751
+ inp[1] = inp[1] * 2 - 1;
115752
+ inp[2] = inp[2] * 2 - 1;
115753
+ let out = create();
115754
+ transformMat4(out, inp, finalMatrix);
115755
+ if (out[3] === 0)
115756
+ return out;
115757
+ out[0] /= out[3];
115758
+ out[1] /= out[3];
115759
+ out[2] /= out[3];
115760
+ return out;
115761
+ }
115762
+ function unpackFloatFromVec4i(val) {
115763
+ var bitSh = [
115764
+ 1 / (256 * 256 * 256),
115765
+ 1 / (256 * 256),
115766
+ 1 / 256,
115767
+ 1
115768
+ ];
115769
+ return (val[0] * bitSh[0] + val[1] * bitSh[1] + val[2] * bitSh[2] + val[3] * bitSh[3]) / 255;
115770
+ }
115771
+ Niivue.prototype.depthPicker = function(leftTopWidthHeight, mvpMatrix) {
115772
+ if (!this.scene.mouseDepthPicker)
115773
+ return;
115774
+ this.scene.mouseDepthPicker = false;
115775
+ let gl = this.gl;
115776
+ const pixelX = this.mousePos[0] * leftTopWidthHeight[2] / leftTopWidthHeight[2];
115777
+ const pixelY = gl.canvas.height - this.mousePos[1] * leftTopWidthHeight[3] / leftTopWidthHeight[3] - 1;
115778
+ const rgbaPixel = new Uint8Array(4);
115779
+ gl.readPixels(pixelX, pixelY, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, rgbaPixel);
115780
+ this.selectedObjectId = rgbaPixel[3];
115781
+ if (this.selectedObjectId === this.VOLUME_ID) {
115782
+ this.scene.crosshairPos = new Float32Array(rgbaPixel.slice(0, 3)).map((x2) => x2 / 255);
115783
+ return;
115784
+ }
115785
+ let depthZ = unpackFloatFromVec4i(rgbaPixel);
115786
+ if (depthZ > 1)
115787
+ return;
115788
+ let fracX = (this.mousePos[0] - leftTopWidthHeight[0]) / leftTopWidthHeight[2];
115789
+ let fracY = (gl.canvas.height - this.mousePos[1] - leftTopWidthHeight[1]) / leftTopWidthHeight[3];
115790
+ let mm = unProject(fracX, fracY, depthZ, mvpMatrix);
115791
+ let frac = this.mm2frac(mm);
115792
+ if (frac[0] < 0 || frac[0] > 1 || frac[1] < 0 || frac[1] > 1 || frac[2] < 0 || frac[2] > 1)
115793
+ return;
115794
+ this.scene.crosshairPos = this.mm2frac(mm);
115795
+ };
115796
+ Niivue.prototype.drawImage3D = function(mvpMatrix, azimuth, elevation) {
115797
+ if (this.volumes.length === 0)
115798
+ return;
115799
+ let gl = this.gl;
115800
+ const rayDir = this.calculateRayDirection(azimuth, elevation);
115801
+ let object3D = this.volumeObject3D;
115802
+ if (object3D) {
115803
+ gl.enable(gl.BLEND);
115804
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
115805
+ gl.enable(gl.CULL_FACE);
115806
+ gl.cullFace(gl.FRONT);
115807
+ let shader = this.renderShader;
115808
+ if (this.scene.mouseDepthPicker)
115809
+ shader = this.pickingImageShader;
115810
+ shader.use(this.gl);
115811
+ gl.uniform1i(shader.backgroundMasksOverlaysLoc, this.backgroundMasksOverlays);
115812
+ if (this.drawBitmap && this.drawBitmap.length > 8)
115813
+ gl.uniform1f(shader.drawOpacityLoc, this.drawOpacity);
115814
+ else
115815
+ gl.uniform1f(shader.drawOpacityLoc, 0);
115816
+ gl.uniformMatrix4fv(shader.mvpLoc, false, mvpMatrix);
115817
+ gl.uniformMatrix4fv(shader.mvpMatRASLoc, false, this.back.matRAS);
115818
+ gl.uniform3fv(shader.rayDirLoc, rayDir);
115819
+ gl.uniform4fv(shader.clipPlaneLoc, this.scene.clipPlane);
115820
+ gl.bindVertexArray(object3D.vao);
115821
+ gl.drawElements(object3D.mode, object3D.indexCount, gl.UNSIGNED_SHORT, 0);
115822
+ gl.bindVertexArray(this.unusedVAO);
115823
+ }
115824
+ };
115825
+ Niivue.prototype.drawOrientationCube = function(leftTopWidthHeight, azimuth = 0, elevation = 0) {
115826
+ if (!this.opts.isOrientCube)
115827
+ return;
115828
+ let sz = 0.05 * Math.min(leftTopWidthHeight[2], leftTopWidthHeight[3]);
115829
+ if (sz < 5)
115830
+ return;
115831
+ let gl = this.gl;
115832
+ gl.enable(gl.CULL_FACE);
115833
+ gl.cullFace(gl.BACK);
115834
+ this.orientCubeShader.use(this.gl);
115835
+ gl.bindVertexArray(this.orientCubeShaderVAO);
115836
+ let modelMatrix = create$2();
115837
+ let projectionMatrix = create$2();
115838
+ ortho(projectionMatrix, 0, gl.canvas.width, 0, gl.canvas.height, -10 * sz, 10 * sz);
115839
+ let translateUpForColorbar = 0;
115840
+ if (leftTopWidthHeight[1] === 0)
115841
+ translateUpForColorbar = gl.canvas.height - this.effectiveCanvasHeight();
115842
+ translate(modelMatrix, modelMatrix, [
115843
+ 1.8 * sz + leftTopWidthHeight[0],
115844
+ translateUpForColorbar + 1.8 * sz + leftTopWidthHeight[1],
115845
+ 0
115846
+ ]);
115847
+ scale$1(modelMatrix, modelMatrix, [sz, sz, sz]);
115848
+ rotateX(modelMatrix, modelMatrix, deg2rad(270 - elevation));
115849
+ rotateZ(modelMatrix, modelMatrix, deg2rad(-azimuth));
115850
+ let modelViewProjectionMatrix = create$2();
115851
+ multiply(modelViewProjectionMatrix, projectionMatrix, modelMatrix);
115852
+ gl.uniformMatrix4fv(this.orientCubeMtxLoc, false, modelViewProjectionMatrix);
115853
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 168);
115854
+ gl.bindVertexArray(this.unusedVAO);
115855
+ this.gl.disable(this.gl.CULL_FACE);
115856
+ };
114184
115857
  Niivue.prototype.draw3D = function(leftTopWidthHeight = [0, 0, 0, 0], mvpMatrix = null, modelMatrix, normalMatrix, azimuth = null, elevation) {
114185
115858
  let isMosaic = azimuth !== null;
114186
115859
  this.setPivot3D();
@@ -114216,62 +115889,20 @@ Niivue.prototype.draw3D = function(leftTopWidthHeight = [0, 0, 0, 0], mvpMatrix
114216
115889
  gl.enable(gl.DEPTH_TEST);
114217
115890
  gl.depthFunc(gl.ALWAYS);
114218
115891
  gl.clearDepth(0);
114219
- if (this.volumes.length === 0) {
114220
- gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
114221
- this.drawMesh3D(true, 1, mvpMatrix, modelMatrix, normalMatrix);
114222
- gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
114223
- return;
114224
- }
114225
- const rayDir = this.calculateRayDirection(azimuth, elevation);
114226
- let object3D = this.volumeObject3D;
114227
- if (this.scene.mouseDepthPicker) {
114228
- gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
114229
- this.scene.mouseDepthPicker = false;
114230
- if (object3D.isVisible && object3D.isPickable) {
114231
- let shader = this.pickingShader;
114232
- shader.use(this.gl);
114233
- gl.enable(gl.CULL_FACE);
114234
- gl.cullFace(gl.FRONT);
114235
- gl.uniformMatrix4fv(shader.mvpUniformLoc, false, mvpMatrix);
114236
- gl.uniform3fv(shader.rayDirUniformLoc, rayDir);
114237
- gl.uniform4fv(shader.clipPlaneUniformLoc, this.scene.clipPlane);
114238
- gl.uniform1i(shader.uniforms["id"], object3D.id);
114239
- gl.bindVertexArray(object3D.vao);
114240
- gl.drawElements(object3D.mode, object3D.indexCount, gl.UNSIGNED_SHORT, 0);
114241
- gl.bindVertexArray(this.unusedVAO);
114242
- }
114243
- const pixelX = this.mousePos[0] * leftTopWidthHeight[2] / leftTopWidthHeight[2];
114244
- const pixelY = gl.canvas.height - this.mousePos[1] * leftTopWidthHeight[3] / leftTopWidthHeight[3] - 1;
114245
- const rgbaPixel = new Uint8Array(4);
114246
- gl.readPixels(pixelX, pixelY, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, rgbaPixel);
114247
- this.selectedObjectId = rgbaPixel[3];
114248
- if (this.selectedObjectId === this.VOLUME_ID) {
114249
- this.scene.crosshairPos = new Float32Array(rgbaPixel.slice(0, 3)).map((x2) => x2 / 255);
114250
- }
114251
- }
114252
- if (this.volumeObject3D.isVisible) {
114253
- gl.enable(gl.BLEND);
114254
- gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
114255
- gl.enable(gl.CULL_FACE);
114256
- gl.cullFace(gl.FRONT);
114257
- let shader = this.renderShader;
114258
- shader.use(this.gl);
114259
- gl.uniform1i(shader.uniforms["backgroundMasksOverlays"], this.backgroundMasksOverlays);
114260
- gl.uniformMatrix4fv(shader.mvpUniformLoc, false, mvpMatrix);
114261
- gl.uniformMatrix4fv(shader.mvpMatRASLoc, false, this.back.matRAS);
114262
- gl.uniform3fv(shader.rayDirUniformLoc, rayDir);
114263
- gl.uniform4fv(shader.clipPlaneUniformLoc, this.scene.clipPlane);
114264
- gl.bindVertexArray(object3D.vao);
114265
- gl.drawElements(object3D.mode, object3D.indexCount, gl.UNSIGNED_SHORT, 0);
114266
- gl.bindVertexArray(this.unusedVAO);
114267
- }
115892
+ if (this.volumes.length > 0)
115893
+ this.drawImage3D(mvpMatrix, azimuth, elevation);
114268
115894
  if (!isMosaic)
114269
115895
  this.drawCrosshairs3D(true, 1, mvpMatrix);
114270
115896
  this.drawMesh3D(true, 1, mvpMatrix, modelMatrix, normalMatrix);
115897
+ if (this.scene.mouseDepthPicker) {
115898
+ this.depthPicker(leftTopWidthHeight, mvpMatrix, true);
115899
+ return;
115900
+ }
114271
115901
  this.drawMesh3D(false, 0.02, mvpMatrix, modelMatrix, normalMatrix);
114272
115902
  if (!isMosaic)
114273
115903
  this.drawCrosshairs3D(false, 0.15, mvpMatrix);
114274
115904
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
115905
+ this.drawOrientationCube(leftTopWidthHeight, azimuth, elevation);
114275
115906
  let posString = "azimuth: " + this.scene.renderAzimuth.toFixed(0) + " elevation: " + this.scene.renderElevation.toFixed(0);
114276
115907
  this.drawGraph();
114277
115908
  this.readyForSync = true;
@@ -114296,13 +115927,17 @@ Niivue.prototype.drawMesh3D = function(isDepthTest = true, alpha = 1, m, modelMt
114296
115927
  gl.depthFunc(gl.ALWAYS);
114297
115928
  }
114298
115929
  gl.disable(gl.CULL_FACE);
114299
- this.meshShader.use(this.gl);
114300
- gl.uniformMatrix4fv(this.meshShader.uniforms["mvpMtx"], false, m);
114301
- gl.uniformMatrix4fv(this.meshShader.uniforms["modelMtx"], false, modelMtx);
114302
- gl.uniformMatrix4fv(this.meshShader.uniforms["normMtx"], false, normMtx);
114303
- gl.uniform1f(this.meshShader.uniforms["opacity"], alpha);
115930
+ let shader = this.meshShaders[0];
114304
115931
  let hasFibers = false;
114305
115932
  for (let i2 = 0; i2 < this.meshes.length; i2++) {
115933
+ shader = this.meshShaders[this.meshes[i2].meshShaderIndex].shader;
115934
+ if (this.scene.mouseDepthPicker)
115935
+ shader = this.pickingMeshShader;
115936
+ shader.use(this.gl);
115937
+ gl.uniformMatrix4fv(shader.mvpLoc, false, m);
115938
+ gl.uniformMatrix4fv(shader.uniforms["modelMtx"], false, modelMtx);
115939
+ gl.uniformMatrix4fv(shader.uniforms["normMtx"], false, normMtx);
115940
+ gl.uniform1f(shader.uniforms["opacity"], alpha);
114306
115941
  if (this.meshes[i2].indexCount < 3)
114307
115942
  continue;
114308
115943
  if (this.meshes[i2].offsetPt0) {
@@ -114318,9 +115953,9 @@ Niivue.prototype.drawMesh3D = function(isDepthTest = true, alpha = 1, m, modelMt
114318
115953
  gl.depthFunc(gl.ALWAYS);
114319
115954
  return;
114320
115955
  }
114321
- let shader = this.fiberShader;
115956
+ shader = this.fiberShader;
114322
115957
  shader.use(this.gl);
114323
- gl.uniformMatrix4fv(shader.uniforms["mvpMtx"], false, m);
115958
+ gl.uniformMatrix4fv(shader.mvpLoc, false, m);
114324
115959
  gl.uniform1f(shader.uniforms["opacity"], alpha);
114325
115960
  for (let i2 = 0; i2 < this.meshes.length; i2++) {
114326
115961
  if (this.meshes[i2].indexCount < 3)
@@ -114335,8 +115970,10 @@ Niivue.prototype.drawMesh3D = function(isDepthTest = true, alpha = 1, m, modelMt
114335
115970
  gl.depthFunc(gl.ALWAYS);
114336
115971
  this.readyForSync = true;
114337
115972
  };
114338
- Niivue.prototype.drawCrosshairs3D = function(isDepthTest = true, alpha = 1, mvpMtx = null) {
114339
- if (!this.opts.show3Dcrosshair)
115973
+ Niivue.prototype.drawCrosshairs3D = function(isDepthTest = true, alpha = 1, mvpMtx = null, is2DView = false) {
115974
+ if (!this.opts.show3Dcrosshair && !is2DView)
115975
+ return;
115976
+ if (this.opts.crosshairWidth <= 0 && is2DView)
114340
115977
  return;
114341
115978
  let gl = this.gl;
114342
115979
  let mm = this.frac2mm(this.scene.crosshairPos);
@@ -114345,8 +115982,14 @@ Niivue.prototype.drawCrosshairs3D = function(isDepthTest = true, alpha = 1, mvpM
114345
115982
  gl.deleteBuffer(this.crosshairs3D.indexBuffer);
114346
115983
  gl.deleteBuffer(this.crosshairs3D.vertexBuffer);
114347
115984
  }
114348
- let radius = 0.5 * Math.min(Math.min(this.back.pixDims[1], this.back.pixDims[2]), this.back.pixDims[3]);
114349
- this.crosshairs3D = NiivueObject3D.generateCrosshairs(this.gl, 1, mm, this.volumeObject3D.extentsMin, this.volumeObject3D.extentsMax, radius);
115985
+ let [mn, mx, range] = this.sceneExtentsMinMax();
115986
+ let radius = 1;
115987
+ if (this.volumes.length > 0)
115988
+ radius = 0.5 * Math.min(Math.min(this.back.pixDims[1], this.back.pixDims[2]), this.back.pixDims[3]);
115989
+ else if (range[0] < 50 || range[0] > 1e3)
115990
+ radius = range[0] * 0.02;
115991
+ radius *= this.opts.crosshairWidth;
115992
+ this.crosshairs3D = NiivueObject3D.generateCrosshairs(this.gl, 1, mm, mn, mx, radius);
114350
115993
  this.crosshairs3D.mm = mm;
114351
115994
  }
114352
115995
  let crosshairsShader = this.surfaceShader;
@@ -114354,7 +115997,7 @@ Niivue.prototype.drawCrosshairs3D = function(isDepthTest = true, alpha = 1, mvpM
114354
115997
  let modelMtx, normMtx;
114355
115998
  if (mvpMtx == null)
114356
115999
  [mvpMtx, modelMtx, normMtx] = this.calculateMvpMatrix(this.crosshairs3D, this.scene.renderAzimuth, this.scene.renderElevation);
114357
- gl.uniformMatrix4fv(crosshairsShader.uniforms["mvpMtx"], false, mvpMtx);
116000
+ gl.uniformMatrix4fv(crosshairsShader.mvpLoc, false, mvpMtx);
114358
116001
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.crosshairs3D.indexBuffer);
114359
116002
  gl.enable(gl.DEPTH_TEST);
114360
116003
  let color = [...this.opts.crosshairColor];
@@ -114367,12 +116010,20 @@ Niivue.prototype.drawCrosshairs3D = function(isDepthTest = true, alpha = 1, mvpM
114367
116010
  gl.depthFunc(gl.ALWAYS);
114368
116011
  }
114369
116012
  color[3] = alpha;
114370
- gl.uniform4fv(crosshairsShader.uniforms["surfaceColor"], color);
116013
+ gl.uniform4fv(crosshairsShader.colorLoc, color);
114371
116014
  gl.bindVertexArray(this.crosshairs3D.vao);
114372
116015
  gl.drawElements(gl.TRIANGLES, this.crosshairs3D.indexCount, gl.UNSIGNED_INT, 0);
114373
116016
  gl.bindVertexArray(this.unusedVAO);
114374
116017
  };
114375
116018
  Niivue.prototype.mm2frac = function(mm, volIdx = 0) {
116019
+ if (this.volumes.length < 1) {
116020
+ let frac2 = [0.1, 0.5, 0.5];
116021
+ let [mn, mx, range] = this.sceneExtentsMinMax();
116022
+ frac2[0] = (mm[0] - mn[0]) / range[0];
116023
+ frac2[1] = (mm[1] - mn[1]) / range[1];
116024
+ frac2[2] = (mm[2] - mn[2]) / range[2];
116025
+ return frac2;
116026
+ }
114376
116027
  let mm4 = fromValues(mm[0], mm[1], mm[2], 1);
114377
116028
  let d = this.volumes[volIdx].dimsRAS;
114378
116029
  let frac = [0, 0, 0];
@@ -114416,10 +116067,18 @@ Niivue.prototype.moveCrosshairInVox = function(x2, y, z) {
114416
116067
  };
114417
116068
  Niivue.prototype.frac2mm = function(frac, volIdx = 0) {
114418
116069
  let pos = fromValues(frac[0], frac[1], frac[2], 1);
114419
- transformMat4(pos, pos, this.volumes[volIdx].frac2mm);
116070
+ if (this.volumes.length > 0)
116071
+ transformMat4(pos, pos, this.volumes[volIdx].frac2mm);
116072
+ else {
116073
+ let [mn, mx] = this.sceneExtentsMinMax();
116074
+ const lerp2 = (x2, y, a) => x2 * (1 - a) + y * a;
116075
+ pos[0] = lerp2(mn[0], mx[0], frac[0]);
116076
+ pos[1] = lerp2(mn[1], mx[1], frac[1]);
116077
+ pos[2] = lerp2(mn[2], mx[2], frac[2]);
116078
+ }
114420
116079
  return pos;
114421
116080
  };
114422
- Niivue.prototype.screenXY2TextureFrac = function(x2, y, i2) {
116081
+ Niivue.prototype.screenXY2TextureFrac = function(x2, y, i2, restrict0to1 = true) {
114423
116082
  let texFrac = [-1, -1, -1];
114424
116083
  var axCorSag = this.screenSlices[i2].axCorSag;
114425
116084
  if (axCorSag > this.sliceTypeSagittal)
@@ -114448,8 +116107,10 @@ Niivue.prototype.screenXY2TextureFrac = function(x2, y, i2) {
114448
116107
  if (axCorSag === this.sliceTypeSagittal)
114449
116108
  xyzMM = swizzleVec3(xyzMM, [2, 0, 1]);
114450
116109
  let xyz = this.mm2frac(xyzMM);
114451
- if (xyz[0] < 0 || xyz[0] > 1 || xyz[1] < 0 || xyz[1] > 1 || xyz[2] < 0 || xyz[2] > 1)
114452
- return texFrac;
116110
+ if (restrict0to1) {
116111
+ if (xyz[0] < 0 || xyz[0] > 1 || xyz[1] < 0 || xyz[1] > 1 || xyz[2] < 0 || xyz[2] > 1)
116112
+ return texFrac;
116113
+ }
114453
116114
  texFrac[0] = xyz[0];
114454
116115
  texFrac[1] = xyz[1];
114455
116116
  texFrac[2] = xyz[2];
@@ -114478,15 +116139,17 @@ Niivue.prototype.canvasPos2frac = function(canvasPos) {
114478
116139
  }
114479
116140
  return [-1, -1, -1];
114480
116141
  };
114481
- Niivue.prototype.scaleSlice = function(w, h) {
114482
- let scalePix = this.gl.canvas.clientWidth / w;
114483
- if (h * scalePix > this.effectiveCanvasHeight())
114484
- scalePix = this.effectiveCanvasHeight() / h;
116142
+ Niivue.prototype.scaleSlice = function(w, h, widthPadPixels = 0, heightPadPixels = 0) {
116143
+ let canvasW = this.canvas.width - widthPadPixels;
116144
+ let canvasH = this.effectiveCanvasHeight() - heightPadPixels;
116145
+ let scalePix = canvasW / w;
116146
+ if (h * scalePix > canvasH)
116147
+ scalePix = canvasH / h;
114485
116148
  let wPix = w * scalePix;
114486
116149
  let hPix = h * scalePix;
114487
116150
  let leftTopWidthHeight = [
114488
- (this.gl.canvas.clientWidth - wPix) * 0.5,
114489
- (this.effectiveCanvasHeight() - hPix) * 0.5,
116151
+ (canvasW - wPix) * 0.5,
116152
+ (canvasH - hPix) * 0.5,
114490
116153
  wPix,
114491
116154
  hPix
114492
116155
  ];
@@ -114506,32 +116169,24 @@ Niivue.prototype.drawThumbnail = function() {
114506
116169
  this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
114507
116170
  this.gl.bindVertexArray(this.unusedVAO);
114508
116171
  };
114509
- Niivue.prototype.drawLine = function(leftTopWidthHeight) {
114510
- this.gl.enable(this.gl.BLEND);
114511
- this.rectShader.use(this.gl);
114512
- this.gl.uniform4fv(this.rectShader.uniforms["lineColor"], this.opts.crosshairColor);
114513
- this.gl.uniform2fv(this.rectShader.uniforms["canvasWidthHeight"], [
114514
- this.gl.canvas.width,
114515
- this.gl.canvas.height
114516
- ]);
114517
- this.gl.uniform4f(this.rectShader.uniforms["leftTopWidthHeight"], leftTopWidthHeight[0], leftTopWidthHeight[1], leftTopWidthHeight[2], leftTopWidthHeight[3]);
114518
- this.gl.bindVertexArray(this.genericVAO);
114519
- this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
114520
- this.gl.bindVertexArray(this.unusedVAO);
114521
- };
114522
- Niivue.prototype.drawLine2DX = function(startXYendXY, thickness = 1) {
116172
+ Niivue.prototype.drawLine = function(startXYendXY, thickness = 1, lineColor = [1, 0, 0, -1]) {
114523
116173
  this.gl.bindVertexArray(this.genericVAO);
114524
116174
  this.lineShader.use(this.gl);
114525
- this.gl.uniform4fv(this.lineShader.uniforms["lineColor"], this.opts.crosshairColor);
114526
- this.gl.uniform2fv(this.lineShader.uniforms["canvasWidthHeight"], [
116175
+ if (lineColor[3] < 0)
116176
+ lineColor = this.opts.crosshairColor;
116177
+ this.gl.uniform4fv(this.lineShader.lineColorLoc, lineColor);
116178
+ this.gl.uniform2fv(this.lineShader.canvasWidthHeightLoc, [
114527
116179
  this.gl.canvas.width,
114528
116180
  this.gl.canvas.height
114529
116181
  ]);
114530
- this.gl.uniform1f(this.lineShader.uniforms["thickness"], thickness);
114531
- this.gl.uniform4fv(this.lineShader.uniforms["startXYendXY"], startXYendXY);
116182
+ this.gl.uniform1f(this.lineShader.thicknessLoc, thickness);
116183
+ this.gl.uniform4fv(this.lineShader.startXYendXYLoc, startXYendXY);
114532
116184
  this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
114533
116185
  this.gl.bindVertexArray(this.unusedVAO);
114534
116186
  };
116187
+ Niivue.prototype.drawGraphLine = function(LTRB, color = [1, 0, 0, 0.5], thickness = 2) {
116188
+ this.drawLine(LTRB, thickness, color);
116189
+ };
114535
116190
  Niivue.prototype.drawCrossLinesMM = function(sliceIndex, axCorSag, axiMM, corMM, sagMM) {
114536
116191
  if (sliceIndex < 0 || this.screenSlices.length <= sliceIndex)
114537
116192
  return;
@@ -114565,12 +116220,11 @@ Niivue.prototype.drawCrossLinesMM = function(sliceIndex, axCorSag, axiMM, corMM,
114565
116220
  right = swizzleVec3(right, [0, 1, 2]);
114566
116221
  left = mm2screen(left);
114567
116222
  right = mm2screen(right);
114568
- this.drawLine2DX([left[0], left[1], right[0], right[1]], thick);
116223
+ this.drawLine([left[0], left[1], right[0], right[1]], thick);
114569
116224
  }
114570
116225
  }
114571
116226
  if (linesH.length > 0 && axCorSag === 1) {
114572
116227
  let fracH = tile.sliceFrac;
114573
- this.frac2mm([0.5, fracH, 0.5]);
114574
116228
  let dimV = 2;
114575
116229
  for (let i2 = 0; i2 < linesH.length; i2++) {
114576
116230
  let mmV = this.frac2mm([0.5, 0.5, 0.5]);
@@ -114583,7 +116237,7 @@ Niivue.prototype.drawCrossLinesMM = function(sliceIndex, axCorSag, axiMM, corMM,
114583
116237
  right = swizzleVec3(right, [0, 2, 1]);
114584
116238
  left = mm2screen(left);
114585
116239
  right = mm2screen(right);
114586
- this.drawLine2DX([left[0], left[1], right[0], right[1]], thick);
116240
+ this.drawLine([left[0], left[1], right[0], right[1]], thick);
114587
116241
  }
114588
116242
  }
114589
116243
  if (linesH.length > 0 && axCorSag === 2) {
@@ -114600,7 +116254,7 @@ Niivue.prototype.drawCrossLinesMM = function(sliceIndex, axCorSag, axiMM, corMM,
114600
116254
  right = swizzleVec3(right, [1, 2, 0]);
114601
116255
  left = mm2screen(left);
114602
116256
  right = mm2screen(right);
114603
- this.drawLine2DX([left[0], left[1], right[0], right[1]], thick);
116257
+ this.drawLine([left[0], left[1], right[0], right[1]], thick);
114604
116258
  }
114605
116259
  }
114606
116260
  if (linesV.length > 0 && axCorSag === 0) {
@@ -114617,7 +116271,7 @@ Niivue.prototype.drawCrossLinesMM = function(sliceIndex, axCorSag, axiMM, corMM,
114617
116271
  right = swizzleVec3(right, [0, 1, 2]);
114618
116272
  left = mm2screen(left);
114619
116273
  right = mm2screen(right);
114620
- this.drawLine2DX([left[0], left[1], right[0], right[1]], thick);
116274
+ this.drawLine([left[0], left[1], right[0], right[1]], thick);
114621
116275
  }
114622
116276
  }
114623
116277
  if (linesV.length > 0 && axCorSag === 1) {
@@ -114634,7 +116288,7 @@ Niivue.prototype.drawCrossLinesMM = function(sliceIndex, axCorSag, axiMM, corMM,
114634
116288
  right = swizzleVec3(right, [0, 2, 1]);
114635
116289
  left = mm2screen(left);
114636
116290
  right = mm2screen(right);
114637
- this.drawLine2DX([left[0], left[1], right[0], right[1]], thick);
116291
+ this.drawLine([left[0], left[1], right[0], right[1]], thick);
114638
116292
  }
114639
116293
  }
114640
116294
  if (linesV.length > 0 && axCorSag === 2) {
@@ -114651,7 +116305,7 @@ Niivue.prototype.drawCrossLinesMM = function(sliceIndex, axCorSag, axiMM, corMM,
114651
116305
  right = swizzleVec3(right, [1, 2, 0]);
114652
116306
  left = mm2screen(left);
114653
116307
  right = mm2screen(right);
114654
- this.drawLine2DX([left[0], left[1], right[0], right[1]], thick);
116308
+ this.drawLine([left[0], left[1], right[0], right[1]], thick);
114655
116309
  }
114656
116310
  }
114657
116311
  };
@@ -114663,7 +116317,6 @@ Niivue.prototype.drawCrossLines = function(sliceIndex, axCorSag, axiMM, corMM, s
114663
116317
  let tile = this.screenSlices[sliceIndex];
114664
116318
  let linesH = corMM.slice();
114665
116319
  let linesV = sagMM.slice();
114666
- Math.max(1, this.opts.crosshairWidth);
114667
116320
  if (axCorSag === this.sliceTypeCoronal)
114668
116321
  linesH = axiMM.slice();
114669
116322
  if (axCorSag === this.sliceTypeSagittal) {
@@ -114677,7 +116330,7 @@ Niivue.prototype.drawCrossLines = function(sliceIndex, axCorSag, axiMM, corMM, s
114677
116330
  for (let i2 = 0; i2 < linesH.length; i2++) {
114678
116331
  mm[sliceDim] = linesH[i2];
114679
116332
  let frac = this.mm2frac(mm);
114680
- this.drawLine([
116333
+ this.drawRect([
114681
116334
  LTWH[0],
114682
116335
  LTWH[1] + LTWH[3] - frac[sliceDim] * LTWH[3],
114683
116336
  LTWH[2],
@@ -114696,14 +116349,14 @@ Niivue.prototype.drawCrossLines = function(sliceIndex, axCorSag, axiMM, corMM, s
114696
116349
  mm[sliceDim] = linesV[i2];
114697
116350
  let frac = this.mm2frac(mm);
114698
116351
  if (isRadiolgical)
114699
- this.drawLine([
116352
+ this.drawRect([
114700
116353
  LTWH[0] + (LTWH[2] - frac[sliceDim] * LTWH[2]),
114701
116354
  LTWH[1],
114702
116355
  1,
114703
116356
  LTWH[3]
114704
116357
  ]);
114705
116358
  else
114706
- this.drawLine([
116359
+ this.drawRect([
114707
116360
  LTWH[0] + frac[sliceDim] * LTWH[2],
114708
116361
  LTWH[1],
114709
116362
  1,
@@ -114825,7 +116478,7 @@ Niivue.prototype.drawScene = function() {
114825
116478
  }
114826
116479
  this.colorbarHeight = 0;
114827
116480
  this.gl.clearColor(this.opts.backColor[0], this.opts.backColor[1], this.opts.backColor[2], this.opts.backColor[3]);
114828
- this.gl.clear(this.gl.COLOR_BUFFER_BIT);
116481
+ this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
114829
116482
  if (this.bmpTexture) {
114830
116483
  this.drawThumbnail();
114831
116484
  return;
@@ -114855,6 +116508,8 @@ Niivue.prototype.drawScene = function() {
114855
116508
  }
114856
116509
  }
114857
116510
  if (this.sliceMosaicString.length < 1 && this.sliceType === this.sliceTypeRender) {
116511
+ if (this.opts.isColorbar)
116512
+ this.reserveColorbarPanel();
114858
116513
  this.screenSlices = [];
114859
116514
  this.draw3D();
114860
116515
  if (this.opts.isColorbar)
@@ -114875,12 +116530,15 @@ Niivue.prototype.drawScene = function() {
114875
116530
  } else if (this.sliceType === this.sliceTypeSagittal) {
114876
116531
  this.draw2D([0, 0, 0, 0], 2);
114877
116532
  } else {
114878
- let { volScale, vox, longestAxis } = this.sliceScale();
114879
- let ltwh = this.scaleSlice(volScale[0] + volScale[1], volScale[1] + volScale[2]);
116533
+ let { volScale } = this.sliceScale();
116534
+ if (typeof this.opts.multiplanarPadPixels !== "number")
116535
+ console.log("multiplanarPadPixels must be numeric");
116536
+ let pad = parseFloat(this.opts.multiplanarPadPixels);
116537
+ let ltwh = this.scaleSlice(volScale[0] + volScale[1], volScale[1] + volScale[2], pad * 1, pad * 1);
114880
116538
  let wX = ltwh[2] * volScale[0] / (volScale[0] + volScale[1]);
114881
- let ltwh3x1 = this.scaleSlice(volScale[0] + volScale[0] + volScale[1], Math.max(volScale[1], volScale[2]));
116539
+ let ltwh3x1 = this.scaleSlice(volScale[0] + volScale[0] + volScale[1], Math.max(volScale[1], volScale[2]), pad * 2);
114882
116540
  let mx = Math.max(Math.max(volScale[1], volScale[2]), volScale[0]);
114883
- let ltwh4x1 = this.scaleSlice(volScale[0] + volScale[0] + volScale[1] + mx, mx);
116541
+ let ltwh4x1 = this.scaleSlice(volScale[0] + volScale[0] + volScale[1] + mx, mx, pad * 3);
114884
116542
  let wX1 = ltwh3x1[2] * volScale[0] / (volScale[0] + volScale[0] + volScale[1]);
114885
116543
  if (wX1 > wX && !this.graph.autoSizeMultiplanar) {
114886
116544
  let pixScale = wX1 / volScale[0];
@@ -114889,24 +116547,24 @@ Niivue.prototype.drawScene = function() {
114889
116547
  if (ltwh3x1[3] === ltwh4x1[3]) {
114890
116548
  ltwh3x1 = ltwh4x1;
114891
116549
  this.draw3D([
114892
- ltwh3x1[0] + wX1 + wX1 + hY1,
116550
+ ltwh3x1[0] + wX1 + wX1 + hY1 + pad * 3,
114893
116551
  ltwh3x1[1],
114894
116552
  ltwh4x1[3],
114895
116553
  ltwh4x1[3]
114896
116554
  ]);
114897
116555
  }
114898
116556
  this.draw2D([ltwh3x1[0], ltwh3x1[1], wX1, hY1], 0);
114899
- this.draw2D([ltwh3x1[0] + wX1, ltwh3x1[1], wX1, hZ1], 1);
114900
- this.draw2D([ltwh3x1[0] + wX1 + wX1, ltwh3x1[1], hY1, hZ1], 2);
116557
+ this.draw2D([ltwh3x1[0] + wX1 + pad, ltwh3x1[1], wX1, hZ1], 1);
116558
+ this.draw2D([ltwh3x1[0] + wX1 + wX1 + pad * 2, ltwh3x1[1], hY1, hZ1], 2);
114901
116559
  } else {
114902
116560
  let wY = ltwh[2] - wX;
114903
116561
  let hY = ltwh[3] * volScale[1] / (volScale[1] + volScale[2]);
114904
116562
  let hZ = ltwh[3] - hY;
114905
- this.draw2D([ltwh[0], ltwh[1] + hZ, wX, hY], 0);
116563
+ this.draw2D([ltwh[0], ltwh[1] + hZ + pad, wX, hY], 0);
114906
116564
  this.draw2D([ltwh[0], ltwh[1], wX, hZ], 1);
114907
- this.draw2D([ltwh[0] + wX, ltwh[1], wY, hZ], 2);
116565
+ this.draw2D([ltwh[0] + wX + pad, ltwh[1], wY, hZ], 2);
114908
116566
  if (!this.graph.autoSizeMultiplanar)
114909
- this.draw3D([ltwh[0] + wX, ltwh[1] + hZ, wY, hY]);
116567
+ this.draw3D([ltwh[0] + wX + pad, ltwh[1] + hZ + pad, wY, hY]);
114910
116568
  }
114911
116569
  }
114912
116570
  }
@@ -114915,6 +116573,15 @@ Niivue.prototype.drawScene = function() {
114915
116573
  if (this.opts.isColorbar)
114916
116574
  this.drawColorbar();
114917
116575
  if (this.isDragging) {
116576
+ if (this.opts.isDragForPanZoom) {
116577
+ this.dragForPanZoom([
116578
+ this.dragStart[0],
116579
+ this.dragStart[1],
116580
+ this.dragEnd[0],
116581
+ this.dragEnd[1]
116582
+ ]);
116583
+ return;
116584
+ }
114918
116585
  if (this.opts.isDragShowsMeasurementTool) {
114919
116586
  this.drawMeasurementTool([
114920
116587
  this.dragStart[0],
@@ -114946,4 +116613,4 @@ Niivue.prototype.drawScene = function() {
114946
116613
  this.readyForSync = true;
114947
116614
  return posString;
114948
116615
  };
114949
- export { NVImage, NVMesh, Niivue };
116616
+ export { NVImage, NVImageFromUrlOptions, NVMesh, NVMeshFromUrlOptions, Niivue, colortables };