@niivue/niivue 0.24.2 → 0.26.3
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/README.md +15 -17
- package/dist/niivue.es.js +2496 -540
- package/dist/niivue.umd.js +295 -125
- package/package.json +1 -1
- package/src/colortables.js +2 -2
- package/src/index.html +3 -2
- package/src/indexDraw.html +11 -4
- package/src/indexMM.html +229 -0
- package/src/indexModulate.html +223 -0
- package/src/niivue.js +2110 -616
- package/src/nvimage.js +132 -30
- package/src/nvmesh.js +33 -14
- package/src/shader-srcs.js +248 -67
package/dist/niivue.es.js
CHANGED
|
@@ -91,7 +91,7 @@ function fromValues$3(m00, m01, m02, m10, m11, m12, m20, m21, m22) {
|
|
|
91
91
|
out[8] = m22;
|
|
92
92
|
return out;
|
|
93
93
|
}
|
|
94
|
-
function multiply$
|
|
94
|
+
function multiply$1(out, a, b) {
|
|
95
95
|
var a00 = a[0], a01 = a[1], a02 = a[2];
|
|
96
96
|
var a10 = a[3], a11 = a[4], a12 = a[5];
|
|
97
97
|
var a20 = a[6], a21 = a[7], a22 = a[8];
|
|
@@ -286,7 +286,7 @@ function invert(out, a) {
|
|
|
286
286
|
out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
|
|
287
287
|
return out;
|
|
288
288
|
}
|
|
289
|
-
function multiply
|
|
289
|
+
function multiply(out, a, b) {
|
|
290
290
|
var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
|
|
291
291
|
var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
|
|
292
292
|
var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
|
|
@@ -500,7 +500,7 @@ function multiplyScalar(out, a, b) {
|
|
|
500
500
|
out[15] = a[15] * b;
|
|
501
501
|
return out;
|
|
502
502
|
}
|
|
503
|
-
var mul
|
|
503
|
+
var mul = multiply;
|
|
504
504
|
function create$1() {
|
|
505
505
|
var out = new ARRAY_TYPE(3);
|
|
506
506
|
if (ARRAY_TYPE != Float32Array) {
|
|
@@ -609,6 +609,7 @@ function angle(a, b) {
|
|
|
609
609
|
var ax = a[0], ay = a[1], az = a[2], bx = b[0], by = b[1], bz = b[2], mag1 = Math.sqrt(ax * ax + ay * ay + az * az), mag2 = Math.sqrt(bx * bx + by * by + bz * bz), mag = mag1 * mag2, cosine = mag && dot(a, b) / mag;
|
|
610
610
|
return Math.acos(Math.min(Math.max(cosine, -1), 1));
|
|
611
611
|
}
|
|
612
|
+
var sub = subtract;
|
|
612
613
|
var len = length$1;
|
|
613
614
|
(function() {
|
|
614
615
|
var vec = create$1();
|
|
@@ -662,13 +663,6 @@ function add(out, a, b) {
|
|
|
662
663
|
out[3] = a[3] + b[3];
|
|
663
664
|
return out;
|
|
664
665
|
}
|
|
665
|
-
function multiply(out, a, b) {
|
|
666
|
-
out[0] = a[0] * b[0];
|
|
667
|
-
out[1] = a[1] * b[1];
|
|
668
|
-
out[2] = a[2] * b[2];
|
|
669
|
-
out[3] = a[3] * b[3];
|
|
670
|
-
return out;
|
|
671
|
-
}
|
|
672
666
|
function transformMat4(out, a, m) {
|
|
673
667
|
var x2 = a[0], y = a[1], z = a[2], w = a[3];
|
|
674
668
|
out[0] = m[0] * x2 + m[4] * y + m[8] * z + m[12] * w;
|
|
@@ -677,7 +671,6 @@ function transformMat4(out, a, m) {
|
|
|
677
671
|
out[3] = m[3] * x2 + m[7] * y + m[11] * z + m[15] * w;
|
|
678
672
|
return out;
|
|
679
673
|
}
|
|
680
|
-
var mul = multiply;
|
|
681
674
|
(function() {
|
|
682
675
|
var vec = create();
|
|
683
676
|
return function(a, stride, offset, count, fn, arg2) {
|
|
@@ -786,7 +779,19 @@ float frac2ndc(vec3 frac) {
|
|
|
786
779
|
|
|
787
780
|
}
|
|
788
781
|
void main() {
|
|
782
|
+
if (fColor.x > 2.0) {
|
|
783
|
+
fColor = vec4(1.0, 0.0, 0.0, 0.5);
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
789
786
|
fColor = vec4(0.0,0.0,0.0,0.0);
|
|
787
|
+
vec4 clipPlaneColorX = clipPlaneColor;
|
|
788
|
+
//if (clipPlaneColor.a < 0.0)
|
|
789
|
+
// clipPlaneColorX.a = - 1.0;
|
|
790
|
+
bool isColorPlaneInVolume = false;
|
|
791
|
+
if (clipPlaneColorX.a < 0.0) {
|
|
792
|
+
isColorPlaneInVolume = true;
|
|
793
|
+
clipPlaneColorX.a = 0.0;
|
|
794
|
+
}
|
|
790
795
|
//fColor = vec4(vColor.rgb, 1.0); return;
|
|
791
796
|
vec3 start = vColor;
|
|
792
797
|
gl_FragDepth = 0.0;
|
|
@@ -813,19 +818,23 @@ void main() {
|
|
|
813
818
|
vec4 deltaDirFast = vec4(dir.xyz * stepSizeFast, stepSizeFast);
|
|
814
819
|
while (samplePos.a <= len) {
|
|
815
820
|
float val = texture(volume, samplePos.xyz).a;
|
|
816
|
-
|
|
821
|
+
if (val > 0.01) {
|
|
822
|
+
break;
|
|
823
|
+
}
|
|
817
824
|
samplePos += deltaDirFast; //advance ray position
|
|
818
825
|
}
|
|
819
826
|
if ((samplePos.a >= len) && ((overlays < 1.0) || (backgroundMasksOverlays > 0))) {
|
|
820
|
-
|
|
821
|
-
fColor +=
|
|
827
|
+
if (isClip){
|
|
828
|
+
fColor += clipPlaneColorX;
|
|
829
|
+
}
|
|
822
830
|
return;
|
|
823
831
|
}
|
|
824
832
|
fColor = vec4(1.0, 1.0, 1.0, 1.0);
|
|
825
833
|
//gl_FragDepth = frac2ndc(samplePos.xyz); //crude due to fast pass resolution
|
|
826
834
|
samplePos -= deltaDirFast;
|
|
827
|
-
|
|
835
|
+
if (samplePos.a < 0.0){
|
|
828
836
|
vec4 samplePos = vec4(start.xyz, 0.0); //ray position
|
|
837
|
+
}
|
|
829
838
|
//end: fast pass
|
|
830
839
|
vec4 colAcc = vec4(0.0,0.0,0.0,0.0);
|
|
831
840
|
vec4 firstHit = vec4(0.0,0.0,0.0,2.0 * lenNoClip);
|
|
@@ -836,23 +845,29 @@ void main() {
|
|
|
836
845
|
while (samplePos.a <= len) {
|
|
837
846
|
vec4 colorSample = texture(volume, samplePos.xyz);
|
|
838
847
|
samplePos += deltaDir; //advance ray position
|
|
839
|
-
if (colorSample.a
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
+
if (colorSample.a >= 0.01) {
|
|
849
|
+
if (firstHit.a > lenNoClip)
|
|
850
|
+
firstHit = samplePos;
|
|
851
|
+
backNearest = min(backNearest, samplePos.a);
|
|
852
|
+
colorSample.a = 1.0-pow((1.0 - colorSample.a), opacityCorrection);
|
|
853
|
+
colorSample.rgb *= colorSample.a;
|
|
854
|
+
colAcc= (1.0 - colAcc.a) * colorSample + colAcc;
|
|
855
|
+
if ( colAcc.a > earlyTermination )
|
|
856
|
+
break;
|
|
857
|
+
}
|
|
848
858
|
}
|
|
849
|
-
|
|
859
|
+
if (firstHit.a < len){
|
|
850
860
|
gl_FragDepth = frac2ndc(firstHit.xyz);
|
|
861
|
+
}
|
|
851
862
|
colAcc.a = (colAcc.a / earlyTermination) * backOpacity;
|
|
852
863
|
fColor = colAcc;
|
|
853
|
-
if (isClip) //CR
|
|
854
|
-
|
|
855
|
-
|
|
864
|
+
//if (isClip) //CR
|
|
865
|
+
if ((isColorPlaneInVolume) && (clipPos.a != samplePos.a) && (abs(firstHit.a - clipPos.a) < deltaDir.a))
|
|
866
|
+
fColor.rgb = mix(fColor.rgb, clipPlaneColorX.rgb, abs(clipPlaneColor.a));
|
|
867
|
+
//fColor.rgb = mix(fColor.rgb, clipPlaneColorX.rgb, clipPlaneColorX.a * 0.65);
|
|
868
|
+
if (overlays < 1.0) {
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
856
871
|
//overlay pass
|
|
857
872
|
len = lenNoClip;
|
|
858
873
|
samplePos = vec4(start.xyz, 0.0); //ray position
|
|
@@ -861,48 +876,56 @@ void main() {
|
|
|
861
876
|
deltaDirFast = vec4(dir.xyz * stepSizeFast, stepSizeFast);
|
|
862
877
|
while (samplePos.a <= len) {
|
|
863
878
|
float val = texture(overlay, samplePos.xyz).a;
|
|
864
|
-
|
|
879
|
+
if (val > 0.01) {
|
|
880
|
+
break;
|
|
881
|
+
}
|
|
865
882
|
samplePos += deltaDirFast; //advance ray position
|
|
866
883
|
}
|
|
867
884
|
if (samplePos.a >= len) {
|
|
868
|
-
|
|
869
|
-
fColor +=
|
|
885
|
+
if (isClip && (fColor.a == 0.0)) {
|
|
886
|
+
fColor += clipPlaneColorX;
|
|
887
|
+
}
|
|
870
888
|
return;
|
|
871
889
|
}
|
|
872
890
|
samplePos -= deltaDirFast;
|
|
873
|
-
|
|
891
|
+
if (samplePos.a < 0.0){
|
|
874
892
|
vec4 samplePos = vec4(start.xyz, 0.0); //ray position
|
|
893
|
+
}
|
|
875
894
|
//end: fast pass
|
|
876
895
|
float overFarthest = len;
|
|
877
896
|
colAcc = vec4(0.0, 0.0, 0.0, 0.0);
|
|
878
897
|
samplePos += deltaDir * ran; //jitter ray
|
|
879
898
|
vec4 overFirstHit = vec4(0.0,0.0,0.0,2.0 * len);
|
|
880
|
-
|
|
899
|
+
if (backgroundMasksOverlays > 0){
|
|
881
900
|
samplePos = firstHit;
|
|
901
|
+
}
|
|
882
902
|
while (samplePos.a <= len) {
|
|
883
903
|
vec4 colorSample = texture(overlay, samplePos.xyz);
|
|
884
904
|
samplePos += deltaDir; //advance ray position
|
|
885
|
-
if (colorSample.a
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
905
|
+
if (colorSample.a >= 0.01) {
|
|
906
|
+
if (overFirstHit.a > len)
|
|
907
|
+
overFirstHit = samplePos;
|
|
908
|
+
colorSample.a = 1.0-pow((1.0 - colorSample.a), opacityCorrection);
|
|
909
|
+
colorSample.rgb *= colorSample.a;
|
|
910
|
+
colAcc= (1.0 - colAcc.a) * colorSample + colAcc;
|
|
911
|
+
overFarthest = samplePos.a;
|
|
912
|
+
if ( colAcc.a > earlyTermination )
|
|
913
|
+
break;
|
|
914
|
+
}
|
|
894
915
|
}
|
|
895
916
|
if (samplePos.a >= len) {
|
|
896
|
-
|
|
897
|
-
fColor +=
|
|
917
|
+
if (isClip && (fColor.a == 0.0)){
|
|
918
|
+
fColor += clipPlaneColorX;
|
|
919
|
+
}
|
|
898
920
|
return;
|
|
899
921
|
}
|
|
900
922
|
//if (overFirstHit.a < len)
|
|
901
|
-
|
|
923
|
+
gl_FragDepth = frac2ndc(overFirstHit.xyz);
|
|
902
924
|
float overMix = colAcc.a;
|
|
903
925
|
float overlayDepth = 0.3;
|
|
904
|
-
|
|
926
|
+
if (fColor.a <= 0.0){
|
|
905
927
|
overMix = 1.0;
|
|
928
|
+
}
|
|
906
929
|
else if (((overFarthest) > backNearest)) {
|
|
907
930
|
float dx = (overFarthest - backNearest)/1.73;
|
|
908
931
|
dx = fColor.a * pow(dx, overlayDepth);
|
|
@@ -917,15 +940,22 @@ layout(location=0) in vec3 pos;
|
|
|
917
940
|
uniform int axCorSag;
|
|
918
941
|
uniform float slice;
|
|
919
942
|
uniform vec2 canvasWidthHeight;
|
|
943
|
+
uniform vec3 panXYscale;
|
|
920
944
|
uniform vec4 leftTopWidthHeight;
|
|
921
945
|
out vec3 texPos;
|
|
922
946
|
void main(void) {
|
|
923
947
|
//convert pixel x,y space 1..canvasWidth,1..canvasHeight to WebGL 1..-1,-1..1
|
|
924
948
|
vec2 frac;
|
|
925
|
-
|
|
926
|
-
frac.
|
|
949
|
+
vec3 vpos = pos;
|
|
950
|
+
frac.x = (leftTopWidthHeight.x + (vpos.x * leftTopWidthHeight.z)) / canvasWidthHeight.x; //0..1
|
|
951
|
+
frac.y = 1.0 - ((leftTopWidthHeight.y + ((1.0 - vpos.y) * leftTopWidthHeight.w)) / canvasWidthHeight.y); //1..0
|
|
952
|
+
//frac.x = pos.x; //0..1
|
|
953
|
+
//frac.y = pos.y; //1..0
|
|
954
|
+
|
|
927
955
|
frac = (frac * 2.0) - 1.0;
|
|
928
956
|
gl_Position = vec4(frac, 0.0, 1.0);
|
|
957
|
+
gl_Position.x += panXYscale.x;
|
|
958
|
+
gl_Position.y += panXYscale.y;
|
|
929
959
|
if (axCorSag == 1)
|
|
930
960
|
texPos = vec3(pos.x, slice, pos.y);
|
|
931
961
|
else if (axCorSag == 2)
|
|
@@ -974,7 +1004,68 @@ void main() {
|
|
|
974
1004
|
color.rgb = mix(color.rgb, ocolor.rgb, ocolor.a);
|
|
975
1005
|
color.a = aout;
|
|
976
1006
|
}`;
|
|
977
|
-
var
|
|
1007
|
+
var vertSliceMMShader = `#version 300 es
|
|
1008
|
+
#line 4
|
|
1009
|
+
layout(location=0) in vec3 pos;
|
|
1010
|
+
uniform int axCorSag;
|
|
1011
|
+
uniform mat4 mvpMtx;
|
|
1012
|
+
uniform mat4 frac2mm;
|
|
1013
|
+
uniform float slice;
|
|
1014
|
+
out vec3 texPos;
|
|
1015
|
+
void main(void) {
|
|
1016
|
+
texPos = vec3(pos.x, pos.y, slice);
|
|
1017
|
+
if (axCorSag > 1)
|
|
1018
|
+
texPos = vec3(slice, pos.x, pos.y);
|
|
1019
|
+
else if (axCorSag > 0)
|
|
1020
|
+
texPos = vec3(pos.x, slice, pos.y);
|
|
1021
|
+
vec4 mm = frac2mm * vec4(texPos, 1.0);
|
|
1022
|
+
//vec4 mm = vec4(texPos, 1.0) * frac2mm;
|
|
1023
|
+
gl_Position = mvpMtx * mm;
|
|
1024
|
+
//gl_Position = mm;
|
|
1025
|
+
}`;
|
|
1026
|
+
var fragSliceMMShader = `#version 300 es
|
|
1027
|
+
#line 228
|
|
1028
|
+
precision highp int;
|
|
1029
|
+
precision highp float;
|
|
1030
|
+
uniform highp sampler3D volume, overlay;
|
|
1031
|
+
uniform int backgroundMasksOverlays;
|
|
1032
|
+
uniform float overlays;
|
|
1033
|
+
uniform float opacity;
|
|
1034
|
+
uniform float drawOpacity;
|
|
1035
|
+
uniform highp sampler3D drawing;
|
|
1036
|
+
in vec3 texPos;
|
|
1037
|
+
out vec4 color;
|
|
1038
|
+
void main() {
|
|
1039
|
+
//color = vec4(1.0, 0.0, 1.0, 1.0);return;
|
|
1040
|
+
vec4 background = texture(volume, texPos);
|
|
1041
|
+
color = vec4(background.rgb, opacity);
|
|
1042
|
+
vec4 ocolor = vec4(0.0);
|
|
1043
|
+
if (overlays > 0.0) {
|
|
1044
|
+
ocolor = texture(overlay, texPos);
|
|
1045
|
+
}
|
|
1046
|
+
float draw = texture(drawing, texPos).r;
|
|
1047
|
+
if (draw > 0.0) {
|
|
1048
|
+
vec3 dcolor = vec3(0.0, 0.0, 0.0);
|
|
1049
|
+
if (draw >= (4.0/255.0))
|
|
1050
|
+
dcolor.rgb = vec3(draw,0.0,draw);
|
|
1051
|
+
else if (draw >= (3.0/255.0))
|
|
1052
|
+
dcolor.b = 1.0;
|
|
1053
|
+
else if (draw >= (2.0/255.0))
|
|
1054
|
+
dcolor.g = 1.0;
|
|
1055
|
+
else
|
|
1056
|
+
dcolor.r = 1.0;
|
|
1057
|
+
color.rgb = mix(color.rgb, dcolor, drawOpacity);
|
|
1058
|
+
color.a = max(drawOpacity, color.a);
|
|
1059
|
+
}
|
|
1060
|
+
if ((backgroundMasksOverlays > 0) && (background.a == 0.0))
|
|
1061
|
+
return;
|
|
1062
|
+
//float aout = ocolor.a + (1.0 - ocolor.a) * color.a;
|
|
1063
|
+
//if (aout <= 0.0) return;
|
|
1064
|
+
//color.rgb = ((ocolor.rgb * ocolor.a) + (color.rgb * color.a * (1.0 - ocolor.a))) / aout;
|
|
1065
|
+
color.rgb = mix(color.rgb, ocolor.rgb, ocolor.a);
|
|
1066
|
+
color.a = 1.0;
|
|
1067
|
+
}`;
|
|
1068
|
+
var fragRectShader = `#version 300 es
|
|
978
1069
|
#line 189
|
|
979
1070
|
precision highp int;
|
|
980
1071
|
precision highp float;
|
|
@@ -1003,10 +1094,13 @@ var fragColorbarShader = `#version 300 es
|
|
|
1003
1094
|
precision highp int;
|
|
1004
1095
|
precision highp float;
|
|
1005
1096
|
uniform highp sampler2D colormap;
|
|
1097
|
+
uniform float layer;
|
|
1006
1098
|
in vec2 vColor;
|
|
1007
1099
|
out vec4 color;
|
|
1008
1100
|
void main() {
|
|
1009
|
-
|
|
1101
|
+
float nlayer = float(textureSize(colormap, 0).y);
|
|
1102
|
+
float fmap = (0.5 + layer) / nlayer;
|
|
1103
|
+
color = vec4(texture(colormap, vec2(vColor.x, fmap)).rgb, 1.0);
|
|
1010
1104
|
}`;
|
|
1011
1105
|
var vertGraphShader = `#version 300 es
|
|
1012
1106
|
#line 229
|
|
@@ -1047,7 +1141,7 @@ void main(void) {
|
|
|
1047
1141
|
frac = (frac * 2.0) - 1.0;
|
|
1048
1142
|
gl_Position = vec4(frac, 0.0, 1.0);
|
|
1049
1143
|
}`;
|
|
1050
|
-
var
|
|
1144
|
+
var vertRectShader = `#version 300 es
|
|
1051
1145
|
#line 229
|
|
1052
1146
|
layout(location=0) in vec3 pos;
|
|
1053
1147
|
uniform vec2 canvasWidthHeight;
|
|
@@ -1060,6 +1154,20 @@ void main(void) {
|
|
|
1060
1154
|
frac = (frac * 2.0) - 1.0;
|
|
1061
1155
|
gl_Position = vec4(frac, 0.0, 1.0);
|
|
1062
1156
|
}`;
|
|
1157
|
+
var vertLineShader = `#version 300 es
|
|
1158
|
+
#line 229
|
|
1159
|
+
layout(location=0) in vec3 pos;
|
|
1160
|
+
uniform vec2 canvasWidthHeight;
|
|
1161
|
+
uniform float thickness;
|
|
1162
|
+
uniform vec4 startXYendXY;
|
|
1163
|
+
void main(void) {
|
|
1164
|
+
vec2 posXY = mix(startXYendXY.xy, startXYendXY.zw, pos.x);
|
|
1165
|
+
vec2 dir = normalize(startXYendXY.xy - startXYendXY.zw);
|
|
1166
|
+
posXY += vec2(-dir.y, dir.x) * thickness * (pos.y - 0.5);
|
|
1167
|
+
posXY.x = (posXY.x) / canvasWidthHeight.x; //0..1
|
|
1168
|
+
posXY.y = 1.0 - (posXY.y / canvasWidthHeight.y); //1..0
|
|
1169
|
+
gl_Position = vec4((posXY * 2.0) - 1.0, 0.0, 1.0);
|
|
1170
|
+
}`;
|
|
1063
1171
|
var vertBmpShader = `#version 300 es
|
|
1064
1172
|
#line 229
|
|
1065
1173
|
layout(location=0) in vec3 pos;
|
|
@@ -1146,7 +1254,7 @@ in vec2 TexCoord;
|
|
|
1146
1254
|
out vec4 FragColor;
|
|
1147
1255
|
uniform float coordZ;
|
|
1148
1256
|
uniform float layer;
|
|
1149
|
-
uniform float numLayers;
|
|
1257
|
+
//uniform float numLayers;
|
|
1150
1258
|
uniform highp sampler2D colormap;
|
|
1151
1259
|
uniform lowp sampler3D blend3D;
|
|
1152
1260
|
uniform float opacity;
|
|
@@ -1176,7 +1284,9 @@ void main(void) {
|
|
|
1176
1284
|
}
|
|
1177
1285
|
idx = ((idx - uint(1)) % uint(100))+uint(1);
|
|
1178
1286
|
float fx = (float(idx)+0.5) / 256.0;
|
|
1179
|
-
float
|
|
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);
|
|
1180
1290
|
FragColor = texture(colormap, vec2(fx, y)).rgba;
|
|
1181
1291
|
FragColor.a *= opacity;
|
|
1182
1292
|
if (layer < 2.0) return;
|
|
@@ -1195,28 +1305,43 @@ in vec2 TexCoord;
|
|
|
1195
1305
|
out vec4 FragColor;
|
|
1196
1306
|
uniform float coordZ;
|
|
1197
1307
|
uniform float layer;
|
|
1198
|
-
uniform float numLayers;
|
|
1308
|
+
//uniform float numLayers;
|
|
1199
1309
|
uniform float scl_slope;
|
|
1200
1310
|
uniform float scl_inter;
|
|
1201
1311
|
uniform float cal_max;
|
|
1202
1312
|
uniform float cal_min;
|
|
1203
1313
|
uniform highp sampler2D colormap;
|
|
1204
1314
|
uniform lowp sampler3D blend3D;
|
|
1315
|
+
uniform int modulation;
|
|
1316
|
+
uniform highp sampler3D modulationVol;
|
|
1205
1317
|
uniform float opacity;
|
|
1206
1318
|
uniform mat4 mtx;
|
|
1207
1319
|
void main(void) {
|
|
1208
1320
|
vec4 vx = vec4(TexCoord.xy, coordZ, 1.0) * mtx;
|
|
1209
1321
|
float f = (scl_slope * float(texture(intensityVol, vx.xyz).r)) + scl_inter;
|
|
1322
|
+
bool isNegative = (f < 0.0);
|
|
1210
1323
|
float r = max(0.00001, abs(cal_max - cal_min));
|
|
1211
1324
|
float mn = min(cal_min, cal_max);
|
|
1212
|
-
|
|
1213
|
-
//float y = 1.0 / numLayers;
|
|
1214
|
-
//y = ((layer + 0.5) * y);
|
|
1325
|
+
float txl = mix(0.0, 1.0, (f - mn) / r);
|
|
1215
1326
|
//https://stackoverflow.com/questions/5879403/opengl-texture-coordinates-in-pixel-space
|
|
1216
|
-
float
|
|
1217
|
-
|
|
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
|
+
}
|
|
1218
1341
|
if (layer > 0.7)
|
|
1219
1342
|
FragColor.a = step(0.00001, FragColor.a);
|
|
1343
|
+
if (modulation > 0)
|
|
1344
|
+
FragColor.rgb *= texture(modulationVol, vx.xyz).r;
|
|
1220
1345
|
FragColor.a *= opacity;
|
|
1221
1346
|
if (layer < 1.0) return;
|
|
1222
1347
|
vec2 texXY = TexCoord.xy*0.5 +vec2(0.5,0.5);
|
|
@@ -1234,7 +1359,7 @@ in vec2 TexCoord;
|
|
|
1234
1359
|
out vec4 FragColor;
|
|
1235
1360
|
uniform float coordZ;
|
|
1236
1361
|
uniform float layer;
|
|
1237
|
-
uniform float numLayers;
|
|
1362
|
+
//uniform float numLayers;
|
|
1238
1363
|
uniform float scl_slope;
|
|
1239
1364
|
uniform float scl_inter;
|
|
1240
1365
|
uniform float cal_max;
|
|
@@ -1244,10 +1369,14 @@ uniform lowp sampler3D blend3D;
|
|
|
1244
1369
|
uniform float opacity;
|
|
1245
1370
|
uniform mat4 mtx;
|
|
1246
1371
|
uniform bool hasAlpha;
|
|
1372
|
+
uniform int modulation;
|
|
1373
|
+
uniform highp sampler3D modulationVol;
|
|
1247
1374
|
void main(void) {
|
|
1248
1375
|
vec4 vx = vec4(TexCoord.xy, coordZ, 1.0) * mtx;
|
|
1249
1376
|
uvec4 aColor = texture(intensityVol, vx.xyz);
|
|
1250
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;
|
|
1251
1380
|
if (!hasAlpha)
|
|
1252
1381
|
FragColor.a = (FragColor.r * 0.21 + FragColor.g * 0.72 + FragColor.b * 0.07);
|
|
1253
1382
|
FragColor.a *= opacity;
|
|
@@ -1290,15 +1419,13 @@ var fragGrowCutShader = `#version 300 es
|
|
|
1290
1419
|
layout(location = 0) out int label;
|
|
1291
1420
|
layout(location = 1) out int strength;
|
|
1292
1421
|
in vec2 TexCoord;
|
|
1293
|
-
|
|
1422
|
+
uniform int finalPass;
|
|
1294
1423
|
uniform float coordZ;
|
|
1295
1424
|
uniform lowp sampler3D in3D;
|
|
1296
1425
|
uniform highp isampler3D inputTexture0; // background
|
|
1297
1426
|
uniform highp isampler3D inputTexture1; // label
|
|
1298
1427
|
uniform highp isampler3D inputTexture2; // strength
|
|
1299
1428
|
void main(void) {
|
|
1300
|
-
//int MAX_STRENGTH = int(255);
|
|
1301
|
-
int MAX_STRENGTH = int(10000);
|
|
1302
1429
|
vec3 interpolatedTextureCoordinate = vec3(TexCoord.xy, coordZ);
|
|
1303
1430
|
ivec3 size = textureSize(inputTexture0, 0);
|
|
1304
1431
|
ivec3 texelIndex = ivec3(floor(interpolatedTextureCoordinate * vec3(size)));
|
|
@@ -1322,6 +1449,29 @@ void main(void) {
|
|
|
1322
1449
|
}
|
|
1323
1450
|
}
|
|
1324
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;
|
|
1325
1475
|
}`;
|
|
1326
1476
|
var vertSurfaceShader = `#version 300 es
|
|
1327
1477
|
layout(location=0) in vec3 pos;
|
|
@@ -1373,6 +1523,22 @@ void main(void) {
|
|
|
1373
1523
|
vV = -vec3(modelMtx*vec4(pos,1.0));
|
|
1374
1524
|
vClr = clr;
|
|
1375
1525
|
}`;
|
|
1526
|
+
var fragMeshDepthShader = `#version 300 es
|
|
1527
|
+
precision highp int;
|
|
1528
|
+
precision highp float;
|
|
1529
|
+
uniform float opacity;
|
|
1530
|
+
out vec4 color;
|
|
1531
|
+
vec4 packFloatToVec4i(const float value) {
|
|
1532
|
+
//this Papaya function uses BSD 3-clause license Copyright (c) 2012-2015, RII-UTHSCSA
|
|
1533
|
+
const vec4 bitSh = vec4(256.0*256.0*256.0, 256.0*256.0, 256.0, 1.0);
|
|
1534
|
+
const vec4 bitMsk = vec4(0.0, 1.0/256.0, 1.0/256.0, 1.0/256.0);
|
|
1535
|
+
vec4 res = fract(value * bitSh);
|
|
1536
|
+
res -= res.xxyz * bitMsk;
|
|
1537
|
+
return res;
|
|
1538
|
+
}
|
|
1539
|
+
void main() {
|
|
1540
|
+
color = packFloatToVec4i(gl_FragCoord.z);
|
|
1541
|
+
}`;
|
|
1376
1542
|
var fragMeshToonShader = `#version 300 es
|
|
1377
1543
|
precision highp int;
|
|
1378
1544
|
precision highp float;
|
|
@@ -1672,6 +1838,18 @@ function __extends(d, b) {
|
|
|
1672
1838
|
}
|
|
1673
1839
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
1674
1840
|
}
|
|
1841
|
+
var __assign = function() {
|
|
1842
|
+
__assign = Object.assign || function __assign2(t) {
|
|
1843
|
+
for (var s, i2 = 1, n = arguments.length; i2 < n; i2++) {
|
|
1844
|
+
s = arguments[i2];
|
|
1845
|
+
for (var p in s)
|
|
1846
|
+
if (Object.prototype.hasOwnProperty.call(s, p))
|
|
1847
|
+
t[p] = s[p];
|
|
1848
|
+
}
|
|
1849
|
+
return t;
|
|
1850
|
+
};
|
|
1851
|
+
return __assign.apply(this, arguments);
|
|
1852
|
+
};
|
|
1675
1853
|
function __values(o) {
|
|
1676
1854
|
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i2 = 0;
|
|
1677
1855
|
if (m)
|
|
@@ -1916,6 +2094,22 @@ function reportUnhandledError(err2) {
|
|
|
1916
2094
|
}
|
|
1917
2095
|
function noop() {
|
|
1918
2096
|
}
|
|
2097
|
+
var COMPLETE_NOTIFICATION = function() {
|
|
2098
|
+
return createNotification("C", void 0, void 0);
|
|
2099
|
+
}();
|
|
2100
|
+
function errorNotification(error) {
|
|
2101
|
+
return createNotification("E", void 0, error);
|
|
2102
|
+
}
|
|
2103
|
+
function nextNotification(value2) {
|
|
2104
|
+
return createNotification("N", value2, void 0);
|
|
2105
|
+
}
|
|
2106
|
+
function createNotification(kind, value2, error) {
|
|
2107
|
+
return {
|
|
2108
|
+
kind,
|
|
2109
|
+
value: value2,
|
|
2110
|
+
error
|
|
2111
|
+
};
|
|
2112
|
+
}
|
|
1919
2113
|
var context = null;
|
|
1920
2114
|
function errorContext(cb) {
|
|
1921
2115
|
if (config.useDeprecatedSynchronousErrorHandling) {
|
|
@@ -1954,24 +2148,24 @@ var Subscriber = function(_super) {
|
|
|
1954
2148
|
return new SafeSubscriber(next, error, complete);
|
|
1955
2149
|
};
|
|
1956
2150
|
Subscriber2.prototype.next = function(value2) {
|
|
1957
|
-
if (this.isStopped)
|
|
1958
|
-
;
|
|
1959
|
-
else {
|
|
2151
|
+
if (this.isStopped) {
|
|
2152
|
+
handleStoppedNotification(nextNotification(value2), this);
|
|
2153
|
+
} else {
|
|
1960
2154
|
this._next(value2);
|
|
1961
2155
|
}
|
|
1962
2156
|
};
|
|
1963
2157
|
Subscriber2.prototype.error = function(err2) {
|
|
1964
|
-
if (this.isStopped)
|
|
1965
|
-
;
|
|
1966
|
-
else {
|
|
2158
|
+
if (this.isStopped) {
|
|
2159
|
+
handleStoppedNotification(errorNotification(err2), this);
|
|
2160
|
+
} else {
|
|
1967
2161
|
this.isStopped = true;
|
|
1968
2162
|
this._error(err2);
|
|
1969
2163
|
}
|
|
1970
2164
|
};
|
|
1971
2165
|
Subscriber2.prototype.complete = function() {
|
|
1972
|
-
if (this.isStopped)
|
|
1973
|
-
;
|
|
1974
|
-
else {
|
|
2166
|
+
if (this.isStopped) {
|
|
2167
|
+
handleStoppedNotification(COMPLETE_NOTIFICATION, this);
|
|
2168
|
+
} else {
|
|
1975
2169
|
this.isStopped = true;
|
|
1976
2170
|
this._complete();
|
|
1977
2171
|
}
|
|
@@ -2084,6 +2278,12 @@ function handleUnhandledError(error) {
|
|
|
2084
2278
|
function defaultErrorHandler(err2) {
|
|
2085
2279
|
throw err2;
|
|
2086
2280
|
}
|
|
2281
|
+
function handleStoppedNotification(notification, subscriber) {
|
|
2282
|
+
var onStoppedNotification = config.onStoppedNotification;
|
|
2283
|
+
onStoppedNotification && timeoutProvider.setTimeout(function() {
|
|
2284
|
+
return onStoppedNotification(notification, subscriber);
|
|
2285
|
+
});
|
|
2286
|
+
}
|
|
2087
2287
|
var EMPTY_OBSERVER = {
|
|
2088
2288
|
closed: true,
|
|
2089
2289
|
next: noop,
|
|
@@ -2361,6 +2561,289 @@ var AnonymousSubject = function(_super) {
|
|
|
2361
2561
|
};
|
|
2362
2562
|
return AnonymousSubject2;
|
|
2363
2563
|
}(Subject);
|
|
2564
|
+
var dateTimestampProvider = {
|
|
2565
|
+
now: function() {
|
|
2566
|
+
return (dateTimestampProvider.delegate || Date).now();
|
|
2567
|
+
},
|
|
2568
|
+
delegate: void 0
|
|
2569
|
+
};
|
|
2570
|
+
var ReplaySubject = function(_super) {
|
|
2571
|
+
__extends(ReplaySubject2, _super);
|
|
2572
|
+
function ReplaySubject2(_bufferSize, _windowTime, _timestampProvider) {
|
|
2573
|
+
if (_bufferSize === void 0) {
|
|
2574
|
+
_bufferSize = Infinity;
|
|
2575
|
+
}
|
|
2576
|
+
if (_windowTime === void 0) {
|
|
2577
|
+
_windowTime = Infinity;
|
|
2578
|
+
}
|
|
2579
|
+
if (_timestampProvider === void 0) {
|
|
2580
|
+
_timestampProvider = dateTimestampProvider;
|
|
2581
|
+
}
|
|
2582
|
+
var _this = _super.call(this) || this;
|
|
2583
|
+
_this._bufferSize = _bufferSize;
|
|
2584
|
+
_this._windowTime = _windowTime;
|
|
2585
|
+
_this._timestampProvider = _timestampProvider;
|
|
2586
|
+
_this._buffer = [];
|
|
2587
|
+
_this._infiniteTimeWindow = true;
|
|
2588
|
+
_this._infiniteTimeWindow = _windowTime === Infinity;
|
|
2589
|
+
_this._bufferSize = Math.max(1, _bufferSize);
|
|
2590
|
+
_this._windowTime = Math.max(1, _windowTime);
|
|
2591
|
+
return _this;
|
|
2592
|
+
}
|
|
2593
|
+
ReplaySubject2.prototype.next = function(value2) {
|
|
2594
|
+
var _a2 = this, isStopped = _a2.isStopped, _buffer = _a2._buffer, _infiniteTimeWindow = _a2._infiniteTimeWindow, _timestampProvider = _a2._timestampProvider, _windowTime = _a2._windowTime;
|
|
2595
|
+
if (!isStopped) {
|
|
2596
|
+
_buffer.push(value2);
|
|
2597
|
+
!_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);
|
|
2598
|
+
}
|
|
2599
|
+
this._trimBuffer();
|
|
2600
|
+
_super.prototype.next.call(this, value2);
|
|
2601
|
+
};
|
|
2602
|
+
ReplaySubject2.prototype._subscribe = function(subscriber) {
|
|
2603
|
+
this._throwIfClosed();
|
|
2604
|
+
this._trimBuffer();
|
|
2605
|
+
var subscription = this._innerSubscribe(subscriber);
|
|
2606
|
+
var _a2 = this, _infiniteTimeWindow = _a2._infiniteTimeWindow, _buffer = _a2._buffer;
|
|
2607
|
+
var copy2 = _buffer.slice();
|
|
2608
|
+
for (var i2 = 0; i2 < copy2.length && !subscriber.closed; i2 += _infiniteTimeWindow ? 1 : 2) {
|
|
2609
|
+
subscriber.next(copy2[i2]);
|
|
2610
|
+
}
|
|
2611
|
+
this._checkFinalizedStatuses(subscriber);
|
|
2612
|
+
return subscription;
|
|
2613
|
+
};
|
|
2614
|
+
ReplaySubject2.prototype._trimBuffer = function() {
|
|
2615
|
+
var _a2 = this, _bufferSize = _a2._bufferSize, _timestampProvider = _a2._timestampProvider, _buffer = _a2._buffer, _infiniteTimeWindow = _a2._infiniteTimeWindow;
|
|
2616
|
+
var adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;
|
|
2617
|
+
_bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);
|
|
2618
|
+
if (!_infiniteTimeWindow) {
|
|
2619
|
+
var now = _timestampProvider.now();
|
|
2620
|
+
var last = 0;
|
|
2621
|
+
for (var i2 = 1; i2 < _buffer.length && _buffer[i2] <= now; i2 += 2) {
|
|
2622
|
+
last = i2;
|
|
2623
|
+
}
|
|
2624
|
+
last && _buffer.splice(0, last + 1);
|
|
2625
|
+
}
|
|
2626
|
+
};
|
|
2627
|
+
return ReplaySubject2;
|
|
2628
|
+
}(Subject);
|
|
2629
|
+
var Action = function(_super) {
|
|
2630
|
+
__extends(Action2, _super);
|
|
2631
|
+
function Action2(scheduler, work) {
|
|
2632
|
+
return _super.call(this) || this;
|
|
2633
|
+
}
|
|
2634
|
+
Action2.prototype.schedule = function(state, delay) {
|
|
2635
|
+
return this;
|
|
2636
|
+
};
|
|
2637
|
+
return Action2;
|
|
2638
|
+
}(Subscription);
|
|
2639
|
+
var intervalProvider = {
|
|
2640
|
+
setInterval: function(handler, timeout) {
|
|
2641
|
+
var args = [];
|
|
2642
|
+
for (var _i = 2; _i < arguments.length; _i++) {
|
|
2643
|
+
args[_i - 2] = arguments[_i];
|
|
2644
|
+
}
|
|
2645
|
+
var delegate = intervalProvider.delegate;
|
|
2646
|
+
if (delegate === null || delegate === void 0 ? void 0 : delegate.setInterval) {
|
|
2647
|
+
return delegate.setInterval.apply(delegate, __spreadArray([handler, timeout], __read(args)));
|
|
2648
|
+
}
|
|
2649
|
+
return setInterval.apply(void 0, __spreadArray([handler, timeout], __read(args)));
|
|
2650
|
+
},
|
|
2651
|
+
clearInterval: function(handle) {
|
|
2652
|
+
var delegate = intervalProvider.delegate;
|
|
2653
|
+
return ((delegate === null || delegate === void 0 ? void 0 : delegate.clearInterval) || clearInterval)(handle);
|
|
2654
|
+
},
|
|
2655
|
+
delegate: void 0
|
|
2656
|
+
};
|
|
2657
|
+
var AsyncAction = function(_super) {
|
|
2658
|
+
__extends(AsyncAction2, _super);
|
|
2659
|
+
function AsyncAction2(scheduler, work) {
|
|
2660
|
+
var _this = _super.call(this, scheduler, work) || this;
|
|
2661
|
+
_this.scheduler = scheduler;
|
|
2662
|
+
_this.work = work;
|
|
2663
|
+
_this.pending = false;
|
|
2664
|
+
return _this;
|
|
2665
|
+
}
|
|
2666
|
+
AsyncAction2.prototype.schedule = function(state, delay) {
|
|
2667
|
+
if (delay === void 0) {
|
|
2668
|
+
delay = 0;
|
|
2669
|
+
}
|
|
2670
|
+
if (this.closed) {
|
|
2671
|
+
return this;
|
|
2672
|
+
}
|
|
2673
|
+
this.state = state;
|
|
2674
|
+
var id = this.id;
|
|
2675
|
+
var scheduler = this.scheduler;
|
|
2676
|
+
if (id != null) {
|
|
2677
|
+
this.id = this.recycleAsyncId(scheduler, id, delay);
|
|
2678
|
+
}
|
|
2679
|
+
this.pending = true;
|
|
2680
|
+
this.delay = delay;
|
|
2681
|
+
this.id = this.id || this.requestAsyncId(scheduler, this.id, delay);
|
|
2682
|
+
return this;
|
|
2683
|
+
};
|
|
2684
|
+
AsyncAction2.prototype.requestAsyncId = function(scheduler, _id, delay) {
|
|
2685
|
+
if (delay === void 0) {
|
|
2686
|
+
delay = 0;
|
|
2687
|
+
}
|
|
2688
|
+
return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);
|
|
2689
|
+
};
|
|
2690
|
+
AsyncAction2.prototype.recycleAsyncId = function(_scheduler, id, delay) {
|
|
2691
|
+
if (delay === void 0) {
|
|
2692
|
+
delay = 0;
|
|
2693
|
+
}
|
|
2694
|
+
if (delay != null && this.delay === delay && this.pending === false) {
|
|
2695
|
+
return id;
|
|
2696
|
+
}
|
|
2697
|
+
intervalProvider.clearInterval(id);
|
|
2698
|
+
return void 0;
|
|
2699
|
+
};
|
|
2700
|
+
AsyncAction2.prototype.execute = function(state, delay) {
|
|
2701
|
+
if (this.closed) {
|
|
2702
|
+
return new Error("executing a cancelled action");
|
|
2703
|
+
}
|
|
2704
|
+
this.pending = false;
|
|
2705
|
+
var error = this._execute(state, delay);
|
|
2706
|
+
if (error) {
|
|
2707
|
+
return error;
|
|
2708
|
+
} else if (this.pending === false && this.id != null) {
|
|
2709
|
+
this.id = this.recycleAsyncId(this.scheduler, this.id, null);
|
|
2710
|
+
}
|
|
2711
|
+
};
|
|
2712
|
+
AsyncAction2.prototype._execute = function(state, _delay) {
|
|
2713
|
+
var errored = false;
|
|
2714
|
+
var errorValue;
|
|
2715
|
+
try {
|
|
2716
|
+
this.work(state);
|
|
2717
|
+
} catch (e) {
|
|
2718
|
+
errored = true;
|
|
2719
|
+
errorValue = e ? e : new Error("Scheduled action threw falsy error");
|
|
2720
|
+
}
|
|
2721
|
+
if (errored) {
|
|
2722
|
+
this.unsubscribe();
|
|
2723
|
+
return errorValue;
|
|
2724
|
+
}
|
|
2725
|
+
};
|
|
2726
|
+
AsyncAction2.prototype.unsubscribe = function() {
|
|
2727
|
+
if (!this.closed) {
|
|
2728
|
+
var _a2 = this, id = _a2.id, scheduler = _a2.scheduler;
|
|
2729
|
+
var actions = scheduler.actions;
|
|
2730
|
+
this.work = this.state = this.scheduler = null;
|
|
2731
|
+
this.pending = false;
|
|
2732
|
+
arrRemove(actions, this);
|
|
2733
|
+
if (id != null) {
|
|
2734
|
+
this.id = this.recycleAsyncId(scheduler, id, null);
|
|
2735
|
+
}
|
|
2736
|
+
this.delay = null;
|
|
2737
|
+
_super.prototype.unsubscribe.call(this);
|
|
2738
|
+
}
|
|
2739
|
+
};
|
|
2740
|
+
return AsyncAction2;
|
|
2741
|
+
}(Action);
|
|
2742
|
+
var Scheduler = function() {
|
|
2743
|
+
function Scheduler2(schedulerActionCtor, now) {
|
|
2744
|
+
if (now === void 0) {
|
|
2745
|
+
now = Scheduler2.now;
|
|
2746
|
+
}
|
|
2747
|
+
this.schedulerActionCtor = schedulerActionCtor;
|
|
2748
|
+
this.now = now;
|
|
2749
|
+
}
|
|
2750
|
+
Scheduler2.prototype.schedule = function(work, delay, state) {
|
|
2751
|
+
if (delay === void 0) {
|
|
2752
|
+
delay = 0;
|
|
2753
|
+
}
|
|
2754
|
+
return new this.schedulerActionCtor(this, work).schedule(state, delay);
|
|
2755
|
+
};
|
|
2756
|
+
Scheduler2.now = dateTimestampProvider.now;
|
|
2757
|
+
return Scheduler2;
|
|
2758
|
+
}();
|
|
2759
|
+
var AsyncScheduler = function(_super) {
|
|
2760
|
+
__extends(AsyncScheduler2, _super);
|
|
2761
|
+
function AsyncScheduler2(SchedulerAction, now) {
|
|
2762
|
+
if (now === void 0) {
|
|
2763
|
+
now = Scheduler.now;
|
|
2764
|
+
}
|
|
2765
|
+
var _this = _super.call(this, SchedulerAction, now) || this;
|
|
2766
|
+
_this.actions = [];
|
|
2767
|
+
_this._active = false;
|
|
2768
|
+
_this._scheduled = void 0;
|
|
2769
|
+
return _this;
|
|
2770
|
+
}
|
|
2771
|
+
AsyncScheduler2.prototype.flush = function(action) {
|
|
2772
|
+
var actions = this.actions;
|
|
2773
|
+
if (this._active) {
|
|
2774
|
+
actions.push(action);
|
|
2775
|
+
return;
|
|
2776
|
+
}
|
|
2777
|
+
var error;
|
|
2778
|
+
this._active = true;
|
|
2779
|
+
do {
|
|
2780
|
+
if (error = action.execute(action.state, action.delay)) {
|
|
2781
|
+
break;
|
|
2782
|
+
}
|
|
2783
|
+
} while (action = actions.shift());
|
|
2784
|
+
this._active = false;
|
|
2785
|
+
if (error) {
|
|
2786
|
+
while (action = actions.shift()) {
|
|
2787
|
+
action.unsubscribe();
|
|
2788
|
+
}
|
|
2789
|
+
throw error;
|
|
2790
|
+
}
|
|
2791
|
+
};
|
|
2792
|
+
return AsyncScheduler2;
|
|
2793
|
+
}(Scheduler);
|
|
2794
|
+
var asyncScheduler = new AsyncScheduler(AsyncAction);
|
|
2795
|
+
var async = asyncScheduler;
|
|
2796
|
+
function isScheduler(value2) {
|
|
2797
|
+
return value2 && isFunction(value2.schedule);
|
|
2798
|
+
}
|
|
2799
|
+
function isValidDate(value2) {
|
|
2800
|
+
return value2 instanceof Date && !isNaN(value2);
|
|
2801
|
+
}
|
|
2802
|
+
function timer(dueTime, intervalOrScheduler, scheduler) {
|
|
2803
|
+
if (dueTime === void 0) {
|
|
2804
|
+
dueTime = 0;
|
|
2805
|
+
}
|
|
2806
|
+
if (scheduler === void 0) {
|
|
2807
|
+
scheduler = async;
|
|
2808
|
+
}
|
|
2809
|
+
var intervalDuration = -1;
|
|
2810
|
+
if (intervalOrScheduler != null) {
|
|
2811
|
+
if (isScheduler(intervalOrScheduler)) {
|
|
2812
|
+
scheduler = intervalOrScheduler;
|
|
2813
|
+
} else {
|
|
2814
|
+
intervalDuration = intervalOrScheduler;
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
return new Observable(function(subscriber) {
|
|
2818
|
+
var due = isValidDate(dueTime) ? +dueTime - scheduler.now() : dueTime;
|
|
2819
|
+
if (due < 0) {
|
|
2820
|
+
due = 0;
|
|
2821
|
+
}
|
|
2822
|
+
var n = 0;
|
|
2823
|
+
return scheduler.schedule(function() {
|
|
2824
|
+
if (!subscriber.closed) {
|
|
2825
|
+
subscriber.next(n++);
|
|
2826
|
+
if (0 <= intervalDuration) {
|
|
2827
|
+
this.schedule(void 0, intervalDuration);
|
|
2828
|
+
} else {
|
|
2829
|
+
subscriber.complete();
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
}, due);
|
|
2833
|
+
});
|
|
2834
|
+
}
|
|
2835
|
+
function interval(period, scheduler) {
|
|
2836
|
+
if (period === void 0) {
|
|
2837
|
+
period = 0;
|
|
2838
|
+
}
|
|
2839
|
+
if (scheduler === void 0) {
|
|
2840
|
+
scheduler = asyncScheduler;
|
|
2841
|
+
}
|
|
2842
|
+
if (period < 0) {
|
|
2843
|
+
period = 0;
|
|
2844
|
+
}
|
|
2845
|
+
return timer(period, period, scheduler);
|
|
2846
|
+
}
|
|
2364
2847
|
var NiivueObject3D = function(id, vertexBuffer, mode, indexCount, indexBuffer = null, vao = null) {
|
|
2365
2848
|
this.BLEND = 1;
|
|
2366
2849
|
this.CULL_FACE = 2;
|
|
@@ -2785,12 +3268,12 @@ var utilities$1 = { exports: {} };
|
|
|
2785
3268
|
return data.getFloat64(start, littleEndian);
|
|
2786
3269
|
};
|
|
2787
3270
|
nifti3.Utils.getLongAt = function(data, start, littleEndian) {
|
|
2788
|
-
var ctr,
|
|
3271
|
+
var ctr, array2 = [], value2 = 0;
|
|
2789
3272
|
for (ctr = 0; ctr < 8; ctr += 1) {
|
|
2790
|
-
|
|
3273
|
+
array2[ctr] = nifti3.Utils.getByteAt(data, start + ctr, littleEndian);
|
|
2791
3274
|
}
|
|
2792
|
-
for (ctr =
|
|
2793
|
-
value2 = value2 * 256 +
|
|
3275
|
+
for (ctr = array2.length - 1; ctr >= 0; ctr--) {
|
|
3276
|
+
value2 = value2 * 256 + array2[ctr];
|
|
2794
3277
|
}
|
|
2795
3278
|
return value2;
|
|
2796
3279
|
};
|
|
@@ -7674,24 +8157,24 @@ var utilities = { exports: {} };
|
|
|
7674
8157
|
}
|
|
7675
8158
|
return tmp.buffer;
|
|
7676
8159
|
};
|
|
7677
|
-
daikon2.Utils.fillBuffer = function(
|
|
8160
|
+
daikon2.Utils.fillBuffer = function(array2, buffer2, offset, numBytes) {
|
|
7678
8161
|
var ctr;
|
|
7679
8162
|
if (numBytes === 1) {
|
|
7680
|
-
for (ctr = 0; ctr <
|
|
7681
|
-
buffer2.setUint8(offset + ctr,
|
|
8163
|
+
for (ctr = 0; ctr < array2.length; ctr += 1) {
|
|
8164
|
+
buffer2.setUint8(offset + ctr, array2[ctr]);
|
|
7682
8165
|
}
|
|
7683
8166
|
} else if (numBytes === 2) {
|
|
7684
|
-
for (ctr = 0; ctr <
|
|
7685
|
-
buffer2.setUint16(offset + ctr * 2,
|
|
8167
|
+
for (ctr = 0; ctr < array2.length; ctr += 1) {
|
|
8168
|
+
buffer2.setUint16(offset + ctr * 2, array2[ctr], true);
|
|
7686
8169
|
}
|
|
7687
8170
|
}
|
|
7688
8171
|
};
|
|
7689
|
-
daikon2.Utils.fillBufferRGB = function(
|
|
7690
|
-
var r, g, b, ctr, numElements = parseInt(
|
|
8172
|
+
daikon2.Utils.fillBufferRGB = function(array2, buffer2, offset) {
|
|
8173
|
+
var r, g, b, ctr, numElements = parseInt(array2.length / 3);
|
|
7691
8174
|
for (ctr = 0; ctr < numElements; ctr += 1) {
|
|
7692
|
-
r =
|
|
7693
|
-
g =
|
|
7694
|
-
b =
|
|
8175
|
+
r = array2[ctr * 3];
|
|
8176
|
+
g = array2[ctr * 3 + 1];
|
|
8177
|
+
b = array2[ctr * 3 + 2];
|
|
7695
8178
|
buffer2.setUint8(offset + ctr, parseInt((r + b + g) / 3), true);
|
|
7696
8179
|
}
|
|
7697
8180
|
};
|
|
@@ -13766,9 +14249,9 @@ var jpegBaseline = { exports: {} };
|
|
|
13766
14249
|
}
|
|
13767
14250
|
function readDataBlock() {
|
|
13768
14251
|
var length2 = readUint16();
|
|
13769
|
-
var
|
|
13770
|
-
offset +=
|
|
13771
|
-
return
|
|
14252
|
+
var array2 = data.subarray(offset, offset + length2 - 2);
|
|
14253
|
+
offset += array2.length;
|
|
14254
|
+
return array2;
|
|
13772
14255
|
}
|
|
13773
14256
|
function prepareComponents(frame2) {
|
|
13774
14257
|
var mcusPerLine = Math.ceil(frame2.samplesPerLine / 8 / frame2.maxH);
|
|
@@ -16853,11 +17336,11 @@ var jpx = { exports: {} };
|
|
|
16853
17336
|
}
|
|
16854
17337
|
return dict.get(name);
|
|
16855
17338
|
};
|
|
16856
|
-
Util2.inherit = function Util_inherit(
|
|
16857
|
-
|
|
16858
|
-
|
|
17339
|
+
Util2.inherit = function Util_inherit(sub2, base, prototype) {
|
|
17340
|
+
sub2.prototype = Object.create(base.prototype);
|
|
17341
|
+
sub2.prototype.constructor = sub2;
|
|
16859
17342
|
for (var prop in prototype) {
|
|
16860
|
-
|
|
17343
|
+
sub2.prototype[prop] = prototype[prop];
|
|
16861
17344
|
}
|
|
16862
17345
|
};
|
|
16863
17346
|
Util2.loadScript = function Util_loadScript(src, callback) {
|
|
@@ -18476,10 +18959,10 @@ var require$$1 = /* @__PURE__ */ getAugmentedNamespace(__viteBrowserExternal$1);
|
|
|
18476
18959
|
return u8array;
|
|
18477
18960
|
}
|
|
18478
18961
|
Module["intArrayFromString"] = intArrayFromString;
|
|
18479
|
-
function intArrayToString(
|
|
18962
|
+
function intArrayToString(array2) {
|
|
18480
18963
|
var ret = [];
|
|
18481
|
-
for (var i2 = 0; i2 <
|
|
18482
|
-
var chr =
|
|
18964
|
+
for (var i2 = 0; i2 < array2.length; i2++) {
|
|
18965
|
+
var chr = array2[i2];
|
|
18483
18966
|
if (chr > 255) {
|
|
18484
18967
|
chr &= 255;
|
|
18485
18968
|
}
|
|
@@ -18489,18 +18972,18 @@ var require$$1 = /* @__PURE__ */ getAugmentedNamespace(__viteBrowserExternal$1);
|
|
|
18489
18972
|
}
|
|
18490
18973
|
Module["intArrayToString"] = intArrayToString;
|
|
18491
18974
|
function writeStringToMemory(string, buffer2, dontAddNull) {
|
|
18492
|
-
var
|
|
18975
|
+
var array2 = intArrayFromString(string, dontAddNull);
|
|
18493
18976
|
var i2 = 0;
|
|
18494
|
-
while (i2 <
|
|
18495
|
-
var chr =
|
|
18977
|
+
while (i2 < array2.length) {
|
|
18978
|
+
var chr = array2[i2];
|
|
18496
18979
|
HEAP8[buffer2 + i2 >> 0] = chr;
|
|
18497
18980
|
i2 = i2 + 1;
|
|
18498
18981
|
}
|
|
18499
18982
|
}
|
|
18500
18983
|
Module["writeStringToMemory"] = writeStringToMemory;
|
|
18501
|
-
function writeArrayToMemory(
|
|
18502
|
-
for (var i2 = 0; i2 <
|
|
18503
|
-
HEAP8[buffer2++ >> 0] =
|
|
18984
|
+
function writeArrayToMemory(array2, buffer2) {
|
|
18985
|
+
for (var i2 = 0; i2 < array2.length; i2++) {
|
|
18986
|
+
HEAP8[buffer2++ >> 0] = array2[i2];
|
|
18504
18987
|
}
|
|
18505
18988
|
}
|
|
18506
18989
|
Module["writeArrayToMemory"] = writeArrayToMemory;
|
|
@@ -18674,9 +19157,9 @@ var require$$1 = /* @__PURE__ */ getAugmentedNamespace(__viteBrowserExternal$1);
|
|
|
18674
19157
|
function __isLeapYear(year) {
|
|
18675
19158
|
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
18676
19159
|
}
|
|
18677
|
-
function __arraySum(
|
|
19160
|
+
function __arraySum(array2, index) {
|
|
18678
19161
|
var sum = 0;
|
|
18679
|
-
for (var i2 = 0; i2 <= index; sum +=
|
|
19162
|
+
for (var i2 = 0; i2 <= index; sum += array2[i2++])
|
|
18680
19163
|
;
|
|
18681
19164
|
return sum;
|
|
18682
19165
|
}
|
|
@@ -103059,7 +103542,7 @@ function isPlatformLittleEndian() {
|
|
|
103059
103542
|
new DataView(buffer2).setInt16(0, 256, true);
|
|
103060
103543
|
return new Int16Array(buffer2)[0] === 256;
|
|
103061
103544
|
}
|
|
103062
|
-
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) {
|
|
103545
|
+
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 = "") {
|
|
103063
103546
|
this.DT_NONE = 0;
|
|
103064
103547
|
this.DT_UNKNOWN = 0;
|
|
103065
103548
|
this.DT_BINARY = 1;
|
|
@@ -103088,7 +103571,9 @@ function NVImage(dataBuffer, name = "", colorMap = "gray", opacity = 1, pairedIm
|
|
|
103088
103571
|
this.percentileFrac = percentileFrac;
|
|
103089
103572
|
this.ignoreZeroVoxels = ignoreZeroVoxels;
|
|
103090
103573
|
this.trustCalMinMax = trustCalMinMax;
|
|
103574
|
+
this.colorMapNegative = colorMapNegative;
|
|
103091
103575
|
this.visible = visible;
|
|
103576
|
+
this.modulationImage = null;
|
|
103092
103577
|
this.series = [];
|
|
103093
103578
|
if (!dataBuffer) {
|
|
103094
103579
|
return;
|
|
@@ -103140,7 +103625,7 @@ function NVImage(dataBuffer, name = "", colorMap = "gray", opacity = 1, pairedIm
|
|
|
103140
103625
|
let nVol4D = imgRaw.byteLength / this.nVox3D / (this.hdr.numBitsPerVoxel / 8);
|
|
103141
103626
|
if (nVol4D !== this.nFrame4D)
|
|
103142
103627
|
console.log("This header does not match voxel data", this.hdr, imgRaw.byteLength);
|
|
103143
|
-
if (this.hdr.intent_code === 1007 && this.nFrame4D === 3 && this.hdr.datatypeCode === this.DT_FLOAT) {
|
|
103628
|
+
if ((this.hdr.intent_code === 1007 || this.hdr.intent_code === 2003) && this.nFrame4D === 3 && this.hdr.datatypeCode === this.DT_FLOAT) {
|
|
103144
103629
|
let tmp = new Float32Array(imgRaw);
|
|
103145
103630
|
let f32 = tmp.slice();
|
|
103146
103631
|
this.hdr.datatypeCode = this.DT_RGB;
|
|
@@ -103352,7 +103837,25 @@ function NVImage(dataBuffer, name = "", colorMap = "gray", opacity = 1, pairedIm
|
|
|
103352
103837
|
this.hdr.cal_max = cal_max;
|
|
103353
103838
|
this.calMinMax();
|
|
103354
103839
|
}
|
|
103840
|
+
NVImage.prototype.computeObliqueAngle = function(mtx44) {
|
|
103841
|
+
let mtx = clone$1(mtx44);
|
|
103842
|
+
transpose(mtx, mtx44);
|
|
103843
|
+
let dxtmp = Math.sqrt(mtx[0] * mtx[0] + mtx[1] * mtx[1] + mtx[2] * mtx[2]);
|
|
103844
|
+
let xmax = Math.max(Math.max(Math.abs(mtx[0]), Math.abs(mtx[1])), Math.abs(mtx[2])) / dxtmp;
|
|
103845
|
+
let dytmp = Math.sqrt(mtx[4] * mtx[4] + mtx[5] * mtx[5] + mtx[6] * mtx[6]);
|
|
103846
|
+
let ymax = Math.max(Math.max(Math.abs(mtx[4]), Math.abs(mtx[5])), Math.abs(mtx[6])) / dytmp;
|
|
103847
|
+
let dztmp = Math.sqrt(mtx[8] * mtx[8] + mtx[9] * mtx[9] + mtx[10] * mtx[10]);
|
|
103848
|
+
let zmax = Math.max(Math.max(Math.abs(mtx[8]), Math.abs(mtx[9])), Math.abs(mtx[10])) / dztmp;
|
|
103849
|
+
let fig_merit = Math.min(Math.min(xmax, ymax), zmax);
|
|
103850
|
+
let oblique_angle = Math.abs(Math.acos(fig_merit) * 180 / 3.141592653);
|
|
103851
|
+
if (oblique_angle > 0.01)
|
|
103852
|
+
console.log("Warning voxels not aligned with world space: " + oblique_angle + " degrees from plumb.\n");
|
|
103853
|
+
else
|
|
103854
|
+
oblique_angle = 0;
|
|
103855
|
+
return oblique_angle;
|
|
103856
|
+
};
|
|
103355
103857
|
NVImage.prototype.calculateOblique = function() {
|
|
103858
|
+
this.oblique_angle = this.computeObliqueAngle(this.matRAS);
|
|
103356
103859
|
let LPI = this.vox2mm([0, 0, 0], this.matRAS);
|
|
103357
103860
|
let X1mm = this.vox2mm([1 / this.pixDimsRAS[1], 0, 0], this.matRAS);
|
|
103358
103861
|
let Y1mm = this.vox2mm([0, 1 / this.pixDimsRAS[2], 0], this.matRAS);
|
|
@@ -103368,6 +103871,21 @@ NVImage.prototype.calculateOblique = function() {
|
|
|
103368
103871
|
let maxShear = Math.max(Math.max(XY, XZ), YZ);
|
|
103369
103872
|
if (maxShear > 0.1)
|
|
103370
103873
|
log$2.debug("Warning: shear detected (gantry tilt) of %f degrees", maxShear);
|
|
103874
|
+
let dim = fromValues(this.dimsRAS[1], this.dimsRAS[2], this.dimsRAS[3], 1);
|
|
103875
|
+
let sform = clone$1(this.matRAS);
|
|
103876
|
+
transpose(sform, sform);
|
|
103877
|
+
let shim = fromValues(-0.5, -0.5, -0.5, 0);
|
|
103878
|
+
translate(sform, sform, shim);
|
|
103879
|
+
sform[0] *= dim[0];
|
|
103880
|
+
sform[1] *= dim[0];
|
|
103881
|
+
sform[2] *= dim[0];
|
|
103882
|
+
sform[4] *= dim[1];
|
|
103883
|
+
sform[5] *= dim[1];
|
|
103884
|
+
sform[6] *= dim[1];
|
|
103885
|
+
sform[8] *= dim[2];
|
|
103886
|
+
sform[9] *= dim[2];
|
|
103887
|
+
sform[10] *= dim[2];
|
|
103888
|
+
this.frac2mm = clone$1(sform);
|
|
103371
103889
|
};
|
|
103372
103890
|
NVImage.prototype.THD_daxes_to_NIFTI = function(xyzDelta, xyzOrigin, orientSpecific) {
|
|
103373
103891
|
let hdr = this.hdr;
|
|
@@ -103768,11 +104286,12 @@ NVImage.prototype.readMGH = function(buffer2) {
|
|
|
103768
104286
|
let hdr = this.hdr;
|
|
103769
104287
|
hdr.dims = [3, 1, 1, 1, 0, 0, 0, 0];
|
|
103770
104288
|
hdr.pixDims = [1, 1, 1, 1, 1, 0, 0, 0];
|
|
103771
|
-
var reader = new DataView(buffer2);
|
|
103772
104289
|
var raw = buffer2;
|
|
104290
|
+
var reader = new DataView(raw);
|
|
103773
104291
|
if (reader.getUint8(0) === 31 && reader.getUint8(1) === 139) {
|
|
103774
|
-
|
|
103775
|
-
|
|
104292
|
+
let raw8 = decompressSync(new Uint8Array(buffer2));
|
|
104293
|
+
raw = raw8.buffer;
|
|
104294
|
+
reader = new DataView(raw);
|
|
103776
104295
|
}
|
|
103777
104296
|
let version = reader.getInt32(0, false);
|
|
103778
104297
|
let width = reader.getInt32(4, false);
|
|
@@ -104062,7 +104581,7 @@ NVImage.prototype.readMHA = function(buffer2, pairedImgData) {
|
|
|
104062
104581
|
line = readStr();
|
|
104063
104582
|
}
|
|
104064
104583
|
let mmMat = fromValues$3(hdr.pixDims[1], 0, 0, 0, hdr.pixDims[2], 0, 0, 0, hdr.pixDims[3]);
|
|
104065
|
-
multiply$
|
|
104584
|
+
multiply$1(mat33, mmMat, mat33);
|
|
104066
104585
|
hdr.affine = [
|
|
104067
104586
|
[-mat33[0], -mat33[3], -mat33[6], -offset[0]],
|
|
104068
104587
|
[-mat33[1], -mat33[4], -mat33[7], -offset[1]],
|
|
@@ -104463,7 +104982,7 @@ NVImage.prototype.readNRRD = function(dataBuffer, pairedImgData) {
|
|
|
104463
104982
|
offset[1] = -offset[1];
|
|
104464
104983
|
if (rot33[8] < 0)
|
|
104465
104984
|
offset[2] = -offset[2];
|
|
104466
|
-
multiply$
|
|
104985
|
+
multiply$1(mat33, rot33, mat33);
|
|
104467
104986
|
let mat = fromValues$2(mat33[0], mat33[3], mat33[6], offset[0], mat33[1], mat33[4], mat33[7], offset[1], mat33[2], mat33[5], mat33[8], offset[2], 0, 0, 0, 1);
|
|
104468
104987
|
let mm000 = this.vox2mm([0, 0, 0], mat);
|
|
104469
104988
|
let mm100 = this.vox2mm([1, 0, 0], mat);
|
|
@@ -104579,7 +105098,7 @@ NVImage.prototype.calculateRAS = function() {
|
|
|
104579
105098
|
rotM[3 + 2 * 4] = (header.dims[perm[2]] - 1) * flip[2];
|
|
104580
105099
|
let residualR = create$2();
|
|
104581
105100
|
invert(residualR, rotM);
|
|
104582
|
-
multiply
|
|
105101
|
+
multiply(residualR, residualR, R2);
|
|
104583
105102
|
this.matRAS = clone$1(residualR);
|
|
104584
105103
|
rotM = fromValues$2(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1);
|
|
104585
105104
|
rotM[perm[0] - 1 + 0 * 4] = -flip[0] * 2 + 1;
|
|
@@ -104881,7 +105400,8 @@ NVImage.loadFromUrl = async function({
|
|
|
104881
105400
|
trustCalMinMax = true,
|
|
104882
105401
|
percentileFrac = 0.02,
|
|
104883
105402
|
ignoreZeroVoxels = false,
|
|
104884
|
-
visible = true
|
|
105403
|
+
visible = true,
|
|
105404
|
+
colorMapNegative = ""
|
|
104885
105405
|
} = {}) {
|
|
104886
105406
|
if (url === "") {
|
|
104887
105407
|
throw Error("url must not be empty");
|
|
@@ -104914,7 +105434,7 @@ NVImage.loadFromUrl = async function({
|
|
|
104914
105434
|
pairedImgData = await resp.arrayBuffer();
|
|
104915
105435
|
}
|
|
104916
105436
|
if (dataBuffer) {
|
|
104917
|
-
nvimage = new NVImage(dataBuffer, name, colorMap, opacity, pairedImgData, cal_min, cal_max, trustCalMinMax, percentileFrac, ignoreZeroVoxels, visible);
|
|
105437
|
+
nvimage = new NVImage(dataBuffer, name, colorMap, opacity, pairedImgData, cal_min, cal_max, trustCalMinMax, percentileFrac, ignoreZeroVoxels, visible, false, false, colorMapNegative);
|
|
104918
105438
|
} else {
|
|
104919
105439
|
alert("Unable to load buffer properly from volume");
|
|
104920
105440
|
}
|
|
@@ -105114,14 +105634,20 @@ function getExtents$1(positions, forceOriginInVolume = true) {
|
|
|
105114
105634
|
return { min: min2, max: max2, furthestVertexFromOrigin, origin };
|
|
105115
105635
|
}
|
|
105116
105636
|
NVImage.prototype.toNiivueObject3D = function(id, gl) {
|
|
105117
|
-
let
|
|
105118
|
-
let
|
|
105119
|
-
let
|
|
105120
|
-
let
|
|
105121
|
-
let
|
|
105122
|
-
let
|
|
105123
|
-
let
|
|
105124
|
-
let
|
|
105637
|
+
let L = -0.5;
|
|
105638
|
+
let P = -0.5;
|
|
105639
|
+
let I2 = -0.5;
|
|
105640
|
+
let R2 = this.dimsRAS[1] - 1 + 0.5;
|
|
105641
|
+
let A2 = this.dimsRAS[2] - 1 + 0.5;
|
|
105642
|
+
let S = this.dimsRAS[3] - 1 + 0.5;
|
|
105643
|
+
let LPI = this.vox2mm([L, P, I2], this.matRAS);
|
|
105644
|
+
let LAI = this.vox2mm([L, A2, I2], this.matRAS);
|
|
105645
|
+
let LPS = this.vox2mm([L, P, S], this.matRAS);
|
|
105646
|
+
let LAS = this.vox2mm([L, A2, S], this.matRAS);
|
|
105647
|
+
let RPI = this.vox2mm([R2, P, I2], this.matRAS);
|
|
105648
|
+
let RAI = this.vox2mm([R2, A2, I2], this.matRAS);
|
|
105649
|
+
let RPS = this.vox2mm([R2, P, S], this.matRAS);
|
|
105650
|
+
let RAS = this.vox2mm([R2, A2, S], this.matRAS);
|
|
105125
105651
|
let posTex = [
|
|
105126
105652
|
...LPS,
|
|
105127
105653
|
...[0, 0, 1],
|
|
@@ -105204,11 +105730,16 @@ NVImage.prototype.toNiivueObject3D = function(id, gl) {
|
|
|
105204
105730
|
...RAI,
|
|
105205
105731
|
...RPI
|
|
105206
105732
|
]);
|
|
105207
|
-
obj3D.extentsMin = extents.min;
|
|
105208
|
-
obj3D.extentsMax = extents.max;
|
|
105733
|
+
obj3D.extentsMin = extents.min.slice();
|
|
105734
|
+
obj3D.extentsMax = extents.max.slice();
|
|
105209
105735
|
obj3D.furthestVertexFromOrigin = extents.furthestVertexFromOrigin;
|
|
105210
105736
|
obj3D.originNegate = clone(extents.origin);
|
|
105211
105737
|
negate(obj3D.originNegate, obj3D.originNegate);
|
|
105738
|
+
obj3D.fieldOfViewDeObliqueMM = [
|
|
105739
|
+
this.dimsRAS[1] * this.pixDimsRAS[1],
|
|
105740
|
+
this.dimsRAS[2] * this.pixDimsRAS[2],
|
|
105741
|
+
this.dimsRAS[3] * this.pixDimsRAS[3]
|
|
105742
|
+
];
|
|
105212
105743
|
return obj3D;
|
|
105213
105744
|
};
|
|
105214
105745
|
const colortables = function() {
|
|
@@ -105260,6 +105791,8 @@ colortables.prototype.makeLut = function(Rs, Gs, Bs, As, Is) {
|
|
|
105260
105791
|
if (this.gamma === 1)
|
|
105261
105792
|
return lut;
|
|
105262
105793
|
for (var i2 = 0; i2 < 255 * 4; i2++) {
|
|
105794
|
+
if (i2 % 4 === 3)
|
|
105795
|
+
continue;
|
|
105263
105796
|
lut[i2] = Math.pow(lut[i2] / 255, 1 / this.gamma) * 255;
|
|
105264
105797
|
}
|
|
105265
105798
|
return lut;
|
|
@@ -105933,7 +106466,7 @@ NVMesh.readTRK = function(buffer2) {
|
|
|
105933
106466
|
identity$1(mat);
|
|
105934
106467
|
}
|
|
105935
106468
|
var vox2mmMat = create$2();
|
|
105936
|
-
mul
|
|
106469
|
+
mul(vox2mmMat, mat, zoomMat);
|
|
105937
106470
|
let i32 = null;
|
|
105938
106471
|
let f32 = null;
|
|
105939
106472
|
i32 = new Int32Array(buffer2.slice(hdr_sz));
|
|
@@ -106753,19 +107286,30 @@ NVMesh.readPLY = function(buffer2) {
|
|
|
106753
107286
|
}
|
|
106754
107287
|
let indices2 = new Int32Array(nface * 3);
|
|
106755
107288
|
let f = 0;
|
|
106756
|
-
let isTriangular2 = true;
|
|
106757
107289
|
for (var i2 = 0; i2 < nface; i2++) {
|
|
106758
107290
|
line = readStr();
|
|
106759
107291
|
let items = line.split(/\s/);
|
|
106760
|
-
|
|
106761
|
-
|
|
106762
|
-
|
|
106763
|
-
|
|
106764
|
-
|
|
106765
|
-
|
|
106766
|
-
|
|
106767
|
-
|
|
106768
|
-
|
|
107292
|
+
let nTri = parseInt(items[0]) - 2;
|
|
107293
|
+
if (nTri < 1)
|
|
107294
|
+
break;
|
|
107295
|
+
if (f + nTri * 3 > indices2.length) {
|
|
107296
|
+
var c2 = new Int32Array(indices2.length + indices2.length);
|
|
107297
|
+
c2.set(indices2);
|
|
107298
|
+
indices2 = c2.slice();
|
|
107299
|
+
}
|
|
107300
|
+
let idx0 = parseInt(items[1]);
|
|
107301
|
+
let idx1 = parseInt(items[2]);
|
|
107302
|
+
for (let j2 = 0; j2 < nTri; j2++) {
|
|
107303
|
+
let idx2 = parseInt(items[3 + j2]);
|
|
107304
|
+
indices2[f + 0] = idx0;
|
|
107305
|
+
indices2[f + 1] = idx1;
|
|
107306
|
+
indices2[f + 2] = idx2;
|
|
107307
|
+
idx1 = idx2;
|
|
107308
|
+
f += 3;
|
|
107309
|
+
}
|
|
107310
|
+
}
|
|
107311
|
+
if (indices2.length !== f)
|
|
107312
|
+
indices2 = indices2.slice(0, f);
|
|
106769
107313
|
return {
|
|
106770
107314
|
positions: positions2,
|
|
106771
107315
|
indices: indices2
|
|
@@ -106956,12 +107500,21 @@ NVMesh.readOBJ = function(buffer2) {
|
|
|
106956
107500
|
}
|
|
106957
107501
|
if (str[0] === "f") {
|
|
106958
107502
|
let items = str.split(" ");
|
|
107503
|
+
let new_t = items.length - 3;
|
|
107504
|
+
if (new_t < 1)
|
|
107505
|
+
break;
|
|
106959
107506
|
let tn = items[1].split("/");
|
|
106960
|
-
|
|
107507
|
+
let t0 = parseInt(tn[0]) - 1;
|
|
106961
107508
|
tn = items[2].split("/");
|
|
106962
|
-
|
|
106963
|
-
|
|
106964
|
-
|
|
107509
|
+
let tprev = parseInt(tn[0]) - 1;
|
|
107510
|
+
for (let j = 0; j < new_t; j++) {
|
|
107511
|
+
tn = items[3 + j].split("/");
|
|
107512
|
+
let tcurr = parseInt(tn[0]) - 1;
|
|
107513
|
+
t.push(t0);
|
|
107514
|
+
t.push(tprev);
|
|
107515
|
+
t.push(tcurr);
|
|
107516
|
+
tprev = tcurr;
|
|
107517
|
+
}
|
|
106965
107518
|
}
|
|
106966
107519
|
}
|
|
106967
107520
|
var positions = new Float32Array(pts);
|
|
@@ -109643,6 +110196,219 @@ var defaultFontMetrics = {
|
|
|
109643
110196
|
glyphs,
|
|
109644
110197
|
kerning
|
|
109645
110198
|
};
|
|
110199
|
+
var DEFAULT_WEBSOCKET_CONFIG = {
|
|
110200
|
+
url: "",
|
|
110201
|
+
deserializer: function(e) {
|
|
110202
|
+
return JSON.parse(e.data);
|
|
110203
|
+
},
|
|
110204
|
+
serializer: function(value2) {
|
|
110205
|
+
return JSON.stringify(value2);
|
|
110206
|
+
}
|
|
110207
|
+
};
|
|
110208
|
+
var WEBSOCKETSUBJECT_INVALID_ERROR_OBJECT = "WebSocketSubject.error must be called with an object with an error code, and an optional reason: { code: number, reason: string }";
|
|
110209
|
+
var WebSocketSubject = function(_super) {
|
|
110210
|
+
__extends(WebSocketSubject2, _super);
|
|
110211
|
+
function WebSocketSubject2(urlConfigOrSource, destination) {
|
|
110212
|
+
var _this = _super.call(this) || this;
|
|
110213
|
+
_this._socket = null;
|
|
110214
|
+
if (urlConfigOrSource instanceof Observable) {
|
|
110215
|
+
_this.destination = destination;
|
|
110216
|
+
_this.source = urlConfigOrSource;
|
|
110217
|
+
} else {
|
|
110218
|
+
var config2 = _this._config = __assign({}, DEFAULT_WEBSOCKET_CONFIG);
|
|
110219
|
+
_this._output = new Subject();
|
|
110220
|
+
if (typeof urlConfigOrSource === "string") {
|
|
110221
|
+
config2.url = urlConfigOrSource;
|
|
110222
|
+
} else {
|
|
110223
|
+
for (var key2 in urlConfigOrSource) {
|
|
110224
|
+
if (urlConfigOrSource.hasOwnProperty(key2)) {
|
|
110225
|
+
config2[key2] = urlConfigOrSource[key2];
|
|
110226
|
+
}
|
|
110227
|
+
}
|
|
110228
|
+
}
|
|
110229
|
+
if (!config2.WebSocketCtor && WebSocket) {
|
|
110230
|
+
config2.WebSocketCtor = WebSocket;
|
|
110231
|
+
} else if (!config2.WebSocketCtor) {
|
|
110232
|
+
throw new Error("no WebSocket constructor can be found");
|
|
110233
|
+
}
|
|
110234
|
+
_this.destination = new ReplaySubject();
|
|
110235
|
+
}
|
|
110236
|
+
return _this;
|
|
110237
|
+
}
|
|
110238
|
+
WebSocketSubject2.prototype.lift = function(operator) {
|
|
110239
|
+
var sock = new WebSocketSubject2(this._config, this.destination);
|
|
110240
|
+
sock.operator = operator;
|
|
110241
|
+
sock.source = this;
|
|
110242
|
+
return sock;
|
|
110243
|
+
};
|
|
110244
|
+
WebSocketSubject2.prototype._resetState = function() {
|
|
110245
|
+
this._socket = null;
|
|
110246
|
+
if (!this.source) {
|
|
110247
|
+
this.destination = new ReplaySubject();
|
|
110248
|
+
}
|
|
110249
|
+
this._output = new Subject();
|
|
110250
|
+
};
|
|
110251
|
+
WebSocketSubject2.prototype.multiplex = function(subMsg, unsubMsg, messageFilter) {
|
|
110252
|
+
var self2 = this;
|
|
110253
|
+
return new Observable(function(observer) {
|
|
110254
|
+
try {
|
|
110255
|
+
self2.next(subMsg());
|
|
110256
|
+
} catch (err2) {
|
|
110257
|
+
observer.error(err2);
|
|
110258
|
+
}
|
|
110259
|
+
var subscription = self2.subscribe({
|
|
110260
|
+
next: function(x2) {
|
|
110261
|
+
try {
|
|
110262
|
+
if (messageFilter(x2)) {
|
|
110263
|
+
observer.next(x2);
|
|
110264
|
+
}
|
|
110265
|
+
} catch (err2) {
|
|
110266
|
+
observer.error(err2);
|
|
110267
|
+
}
|
|
110268
|
+
},
|
|
110269
|
+
error: function(err2) {
|
|
110270
|
+
return observer.error(err2);
|
|
110271
|
+
},
|
|
110272
|
+
complete: function() {
|
|
110273
|
+
return observer.complete();
|
|
110274
|
+
}
|
|
110275
|
+
});
|
|
110276
|
+
return function() {
|
|
110277
|
+
try {
|
|
110278
|
+
self2.next(unsubMsg());
|
|
110279
|
+
} catch (err2) {
|
|
110280
|
+
observer.error(err2);
|
|
110281
|
+
}
|
|
110282
|
+
subscription.unsubscribe();
|
|
110283
|
+
};
|
|
110284
|
+
});
|
|
110285
|
+
};
|
|
110286
|
+
WebSocketSubject2.prototype._connectSocket = function() {
|
|
110287
|
+
var _this = this;
|
|
110288
|
+
var _a2 = this._config, WebSocketCtor = _a2.WebSocketCtor, protocol = _a2.protocol, url = _a2.url, binaryType = _a2.binaryType;
|
|
110289
|
+
var observer = this._output;
|
|
110290
|
+
var socket = null;
|
|
110291
|
+
try {
|
|
110292
|
+
socket = protocol ? new WebSocketCtor(url, protocol) : new WebSocketCtor(url);
|
|
110293
|
+
this._socket = socket;
|
|
110294
|
+
if (binaryType) {
|
|
110295
|
+
this._socket.binaryType = binaryType;
|
|
110296
|
+
}
|
|
110297
|
+
} catch (e) {
|
|
110298
|
+
observer.error(e);
|
|
110299
|
+
return;
|
|
110300
|
+
}
|
|
110301
|
+
var subscription = new Subscription(function() {
|
|
110302
|
+
_this._socket = null;
|
|
110303
|
+
if (socket && socket.readyState === 1) {
|
|
110304
|
+
socket.close();
|
|
110305
|
+
}
|
|
110306
|
+
});
|
|
110307
|
+
socket.onopen = function(evt) {
|
|
110308
|
+
var _socket = _this._socket;
|
|
110309
|
+
if (!_socket) {
|
|
110310
|
+
socket.close();
|
|
110311
|
+
_this._resetState();
|
|
110312
|
+
return;
|
|
110313
|
+
}
|
|
110314
|
+
var openObserver = _this._config.openObserver;
|
|
110315
|
+
if (openObserver) {
|
|
110316
|
+
openObserver.next(evt);
|
|
110317
|
+
}
|
|
110318
|
+
var queue = _this.destination;
|
|
110319
|
+
_this.destination = Subscriber.create(function(x2) {
|
|
110320
|
+
if (socket.readyState === 1) {
|
|
110321
|
+
try {
|
|
110322
|
+
var serializer = _this._config.serializer;
|
|
110323
|
+
socket.send(serializer(x2));
|
|
110324
|
+
} catch (e) {
|
|
110325
|
+
_this.destination.error(e);
|
|
110326
|
+
}
|
|
110327
|
+
}
|
|
110328
|
+
}, function(err2) {
|
|
110329
|
+
var closingObserver = _this._config.closingObserver;
|
|
110330
|
+
if (closingObserver) {
|
|
110331
|
+
closingObserver.next(void 0);
|
|
110332
|
+
}
|
|
110333
|
+
if (err2 && err2.code) {
|
|
110334
|
+
socket.close(err2.code, err2.reason);
|
|
110335
|
+
} else {
|
|
110336
|
+
observer.error(new TypeError(WEBSOCKETSUBJECT_INVALID_ERROR_OBJECT));
|
|
110337
|
+
}
|
|
110338
|
+
_this._resetState();
|
|
110339
|
+
}, function() {
|
|
110340
|
+
var closingObserver = _this._config.closingObserver;
|
|
110341
|
+
if (closingObserver) {
|
|
110342
|
+
closingObserver.next(void 0);
|
|
110343
|
+
}
|
|
110344
|
+
socket.close();
|
|
110345
|
+
_this._resetState();
|
|
110346
|
+
});
|
|
110347
|
+
if (queue && queue instanceof ReplaySubject) {
|
|
110348
|
+
subscription.add(queue.subscribe(_this.destination));
|
|
110349
|
+
}
|
|
110350
|
+
};
|
|
110351
|
+
socket.onerror = function(e) {
|
|
110352
|
+
_this._resetState();
|
|
110353
|
+
observer.error(e);
|
|
110354
|
+
};
|
|
110355
|
+
socket.onclose = function(e) {
|
|
110356
|
+
if (socket === _this._socket) {
|
|
110357
|
+
_this._resetState();
|
|
110358
|
+
}
|
|
110359
|
+
var closeObserver = _this._config.closeObserver;
|
|
110360
|
+
if (closeObserver) {
|
|
110361
|
+
closeObserver.next(e);
|
|
110362
|
+
}
|
|
110363
|
+
if (e.wasClean) {
|
|
110364
|
+
observer.complete();
|
|
110365
|
+
} else {
|
|
110366
|
+
observer.error(e);
|
|
110367
|
+
}
|
|
110368
|
+
};
|
|
110369
|
+
socket.onmessage = function(e) {
|
|
110370
|
+
try {
|
|
110371
|
+
var deserializer = _this._config.deserializer;
|
|
110372
|
+
observer.next(deserializer(e));
|
|
110373
|
+
} catch (err2) {
|
|
110374
|
+
observer.error(err2);
|
|
110375
|
+
}
|
|
110376
|
+
};
|
|
110377
|
+
};
|
|
110378
|
+
WebSocketSubject2.prototype._subscribe = function(subscriber) {
|
|
110379
|
+
var _this = this;
|
|
110380
|
+
var source = this.source;
|
|
110381
|
+
if (source) {
|
|
110382
|
+
return source.subscribe(subscriber);
|
|
110383
|
+
}
|
|
110384
|
+
if (!this._socket) {
|
|
110385
|
+
this._connectSocket();
|
|
110386
|
+
}
|
|
110387
|
+
this._output.subscribe(subscriber);
|
|
110388
|
+
subscriber.add(function() {
|
|
110389
|
+
var _socket = _this._socket;
|
|
110390
|
+
if (_this._output.observers.length === 0) {
|
|
110391
|
+
if (_socket && (_socket.readyState === 1 || _socket.readyState === 0)) {
|
|
110392
|
+
_socket.close();
|
|
110393
|
+
}
|
|
110394
|
+
_this._resetState();
|
|
110395
|
+
}
|
|
110396
|
+
});
|
|
110397
|
+
return subscriber;
|
|
110398
|
+
};
|
|
110399
|
+
WebSocketSubject2.prototype.unsubscribe = function() {
|
|
110400
|
+
var _socket = this._socket;
|
|
110401
|
+
if (_socket && (_socket.readyState === 1 || _socket.readyState === 0)) {
|
|
110402
|
+
_socket.close();
|
|
110403
|
+
}
|
|
110404
|
+
this._resetState();
|
|
110405
|
+
_super.prototype.unsubscribe.call(this);
|
|
110406
|
+
};
|
|
110407
|
+
return WebSocketSubject2;
|
|
110408
|
+
}(AnonymousSubject);
|
|
110409
|
+
function webSocket(urlConfigOrSource) {
|
|
110410
|
+
return new WebSocketSubject(urlConfigOrSource);
|
|
110411
|
+
}
|
|
109646
110412
|
const log = new Log();
|
|
109647
110413
|
const cmapper = new colortables();
|
|
109648
110414
|
function Niivue(options = {}) {
|
|
@@ -109651,11 +110417,13 @@ function Niivue(options = {}) {
|
|
|
109651
110417
|
textHeight: 0.06,
|
|
109652
110418
|
colorbarHeight: 0.05,
|
|
109653
110419
|
crosshairWidth: 1,
|
|
110420
|
+
rulerWidth: 4,
|
|
109654
110421
|
show3Dcrosshair: false,
|
|
109655
110422
|
backColor: [0, 0, 0, 1],
|
|
109656
110423
|
crosshairColor: [1, 0, 0, 1],
|
|
109657
110424
|
selectionBoxColor: [1, 1, 1, 0.5],
|
|
109658
110425
|
clipPlaneColor: [0.7, 0, 0.7, 0.5],
|
|
110426
|
+
rulerColor: [1, 0, 0, 0.8],
|
|
109659
110427
|
colorBarMargin: 0.05,
|
|
109660
110428
|
trustCalMinMax: true,
|
|
109661
110429
|
clipPlaneHotKey: "KeyC",
|
|
@@ -109663,14 +110431,27 @@ function Niivue(options = {}) {
|
|
|
109663
110431
|
keyDebounceTime: 50,
|
|
109664
110432
|
isNearestInterpolation: false,
|
|
109665
110433
|
isAtlasOutline: false,
|
|
110434
|
+
isRuler: false,
|
|
110435
|
+
isColorbar: false,
|
|
109666
110436
|
isRadiologicalConvention: false,
|
|
110437
|
+
meshThicknessOn2D: Infinity,
|
|
110438
|
+
isDragShowsMeasurementTool: false,
|
|
110439
|
+
isCornerOrientationText: false,
|
|
110440
|
+
sagittalNoseLeft: false,
|
|
110441
|
+
isSliceMM: false,
|
|
109667
110442
|
logging: false,
|
|
109668
110443
|
loadingText: "waiting for images...",
|
|
109669
110444
|
dragAndDropEnabled: true,
|
|
109670
110445
|
drawingEnabled: false,
|
|
109671
110446
|
penValue: 1,
|
|
109672
110447
|
isFilledPen: false,
|
|
109673
|
-
thumbnail: ""
|
|
110448
|
+
thumbnail: "",
|
|
110449
|
+
onLocationChange: () => {
|
|
110450
|
+
},
|
|
110451
|
+
onIntensityChange: () => {
|
|
110452
|
+
},
|
|
110453
|
+
onImageLoaded: () => {
|
|
110454
|
+
}
|
|
109674
110455
|
};
|
|
109675
110456
|
this.canvas = null;
|
|
109676
110457
|
this.gl = null;
|
|
@@ -109678,8 +110459,11 @@ function Niivue(options = {}) {
|
|
|
109678
110459
|
this.volumeTexture = null;
|
|
109679
110460
|
this.drawTexture = null;
|
|
109680
110461
|
this.drawBitmap = null;
|
|
109681
|
-
this.
|
|
110462
|
+
this.maxDrawUndoBitmaps = 8;
|
|
110463
|
+
this.currentDrawUndoBitmap = this.maxDrawUndoBitmaps;
|
|
110464
|
+
this.drawUndoBitmaps = [];
|
|
109682
110465
|
this.drawOpacity = 0.8;
|
|
110466
|
+
this.colorbarHeight = 0;
|
|
109683
110467
|
this.drawPenLocation = [NaN, NaN, NaN];
|
|
109684
110468
|
this.drawPenAxCorSag = -1;
|
|
109685
110469
|
this.drawFillOverwrites = true;
|
|
@@ -109687,7 +110471,8 @@ function Niivue(options = {}) {
|
|
|
109687
110471
|
this.overlayTexture = null;
|
|
109688
110472
|
this.overlayTextureID = [];
|
|
109689
110473
|
this.sliceShader = null;
|
|
109690
|
-
this.
|
|
110474
|
+
this.sliceMMShader = null;
|
|
110475
|
+
this.rectShader = null;
|
|
109691
110476
|
this.graphShader = null;
|
|
109692
110477
|
this.renderShader = null;
|
|
109693
110478
|
this.pickingShader = null;
|
|
@@ -109720,6 +110505,7 @@ function Niivue(options = {}) {
|
|
|
109720
110505
|
this.sliceTypeSagittal = 2;
|
|
109721
110506
|
this.sliceTypeMultiplanar = 3;
|
|
109722
110507
|
this.sliceTypeRender = 4;
|
|
110508
|
+
this.sliceMosaicString = "";
|
|
109723
110509
|
this.sliceType = this.sliceTypeMultiplanar;
|
|
109724
110510
|
this.scene = {};
|
|
109725
110511
|
this.syncOpts = {};
|
|
@@ -109736,6 +110522,7 @@ function Niivue(options = {}) {
|
|
|
109736
110522
|
this.scene.mouseButtonLeftDown = false;
|
|
109737
110523
|
this.scene.mouseButtonRightDown = false;
|
|
109738
110524
|
this.scene.mouseDepthPicker = false;
|
|
110525
|
+
this.scene.pan2Dxyzmm = [0, 0, 0, 1];
|
|
109739
110526
|
this.scene.scale2D = 1;
|
|
109740
110527
|
this.scene.prevX = 0;
|
|
109741
110528
|
this.scene.prevY = 0;
|
|
@@ -109752,13 +110539,7 @@ function Niivue(options = {}) {
|
|
|
109752
110539
|
this.volScale = [];
|
|
109753
110540
|
this.vox = [];
|
|
109754
110541
|
this.mousePos = [0, 0];
|
|
109755
|
-
this.
|
|
109756
|
-
this.screenSlices = [
|
|
109757
|
-
{ leftTopWidthHeight: [1, 0, 0, 1], axCorSag: this.sliceTypeAxial },
|
|
109758
|
-
{ leftTopWidthHeight: [1, 0, 0, 1], axCorSag: this.sliceTypeAxial },
|
|
109759
|
-
{ leftTopWidthHeight: [1, 0, 0, 1], axCorSag: this.sliceTypeAxial },
|
|
109760
|
-
{ leftTopWidthHeight: [1, 0, 0, 1], axCorSag: this.sliceTypeAxial }
|
|
109761
|
-
];
|
|
110542
|
+
this.screenSlices = [];
|
|
109762
110543
|
this.isDragging = false;
|
|
109763
110544
|
this.dragStart = [0, 0];
|
|
109764
110545
|
this.dragEnd = [0, 0];
|
|
@@ -109768,10 +110549,7 @@ function Niivue(options = {}) {
|
|
|
109768
110549
|
this.volumeObject3D = null;
|
|
109769
110550
|
this.pivot3D = [0, 0, 0];
|
|
109770
110551
|
this.furthestFromPivot = 10;
|
|
109771
|
-
this.intensityRange$ = new Subject();
|
|
109772
|
-
this.scene.location$ = new Subject();
|
|
109773
110552
|
this.scene.loading$ = new Subject();
|
|
109774
|
-
this.imageLoaded$ = new Subject();
|
|
109775
110553
|
this.currentClipPlaneIndex = 0;
|
|
109776
110554
|
this.lastCalled = new Date().getTime();
|
|
109777
110555
|
this.multiTouchGesture = false;
|
|
@@ -109810,8 +110588,18 @@ function Niivue(options = {}) {
|
|
|
109810
110588
|
{
|
|
109811
110589
|
Name: "Toon",
|
|
109812
110590
|
Frag: fragMeshToonShader
|
|
110591
|
+
},
|
|
110592
|
+
{
|
|
110593
|
+
Name: "Depth",
|
|
110594
|
+
Frag: fragMeshDepthShader
|
|
109813
110595
|
}
|
|
109814
110596
|
];
|
|
110597
|
+
this.isController = false;
|
|
110598
|
+
this.isInSession = false;
|
|
110599
|
+
this.sessionKey = "";
|
|
110600
|
+
this.sessionUrl = "";
|
|
110601
|
+
this.serverConnection$ = null;
|
|
110602
|
+
this.interval$ = null;
|
|
109815
110603
|
this.initialized = false;
|
|
109816
110604
|
for (let prop in this.defaults) {
|
|
109817
110605
|
this.opts[prop] = options[prop] === void 0 ? this.defaults[prop] : options[prop];
|
|
@@ -109825,13 +110613,32 @@ function Niivue(options = {}) {
|
|
|
109825
110613
|
this.loadingText = this.opts.loadingText;
|
|
109826
110614
|
log.setLogLevel(this.opts.logging);
|
|
109827
110615
|
this.eventsToSubjects = {
|
|
109828
|
-
|
|
109829
|
-
loading: this.scene.loading$,
|
|
109830
|
-
imageLoaded: this.imageLoaded$,
|
|
109831
|
-
intensityRange: this.intensityRange$
|
|
110616
|
+
loading: this.scene.loading$
|
|
109832
110617
|
};
|
|
109833
110618
|
this.subscriptions = [];
|
|
109834
110619
|
}
|
|
110620
|
+
Niivue.prototype.saveScene = function(filename = "") {
|
|
110621
|
+
function saveBlob(blob, name) {
|
|
110622
|
+
const a = document.createElement("a");
|
|
110623
|
+
document.body.appendChild(a);
|
|
110624
|
+
a.style.display = "none";
|
|
110625
|
+
const url = window.URL.createObjectURL(blob);
|
|
110626
|
+
a.href = url;
|
|
110627
|
+
a.download = name;
|
|
110628
|
+
a.click();
|
|
110629
|
+
a.remove();
|
|
110630
|
+
}
|
|
110631
|
+
let canvas = this.canvas;
|
|
110632
|
+
this.drawScene();
|
|
110633
|
+
canvas.toBlob((blob) => {
|
|
110634
|
+
if (filename === "") {
|
|
110635
|
+
filename = `niivue-screenshot-${new Date().toString()}.png`;
|
|
110636
|
+
filename = filename.replace(/\s/g, "_");
|
|
110637
|
+
}
|
|
110638
|
+
console.log(filename);
|
|
110639
|
+
saveBlob(blob, filename);
|
|
110640
|
+
});
|
|
110641
|
+
};
|
|
109835
110642
|
Niivue.prototype.attachTo = async function(id) {
|
|
109836
110643
|
await this.attachToCanvas(document.getElementById(id));
|
|
109837
110644
|
log.debug("attached to element with id: ", id);
|
|
@@ -109902,6 +110709,85 @@ Niivue.prototype.sync = function() {
|
|
|
109902
110709
|
}
|
|
109903
110710
|
this.otherNV.drawScene();
|
|
109904
110711
|
};
|
|
110712
|
+
Niivue.prototype.connectToServer = function(wsServerUrl, sessionName) {
|
|
110713
|
+
const url = new URL(wsServerUrl);
|
|
110714
|
+
url.pathname = "websockets";
|
|
110715
|
+
url.search = "?session=" + sessionName;
|
|
110716
|
+
this.serverConnection$ = webSocket(url.href);
|
|
110717
|
+
console.log(url.href);
|
|
110718
|
+
};
|
|
110719
|
+
Niivue.prototype.setUpdateInterval = function() {
|
|
110720
|
+
this.interval$ = interval(300);
|
|
110721
|
+
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
|
+
});
|
|
110730
|
+
});
|
|
110731
|
+
};
|
|
110732
|
+
Niivue.prototype.subscribeToServer = function(sessionCreatedCallback, sessionJoinedCallback) {
|
|
110733
|
+
this.serverConnection$.subscribe({
|
|
110734
|
+
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
|
+
}
|
|
110765
|
+
},
|
|
110766
|
+
error: (err2) => console.log(err2),
|
|
110767
|
+
complete: () => console.log("complete")
|
|
110768
|
+
});
|
|
110769
|
+
};
|
|
110770
|
+
Niivue.prototype.createSession = function(wsServerUrl, sessionName, sessionCreatedCallback) {
|
|
110771
|
+
this.connectToServer(wsServerUrl, sessionName);
|
|
110772
|
+
this.subscribeToServer(sessionCreatedCallback);
|
|
110773
|
+
this.serverConnection$.next({
|
|
110774
|
+
op: "create"
|
|
110775
|
+
});
|
|
110776
|
+
};
|
|
110777
|
+
Niivue.prototype.joinSession = function(wsServerUrl, sessionName, key2, sessionJoinedCallback) {
|
|
110778
|
+
this.connectToServer(wsServerUrl, sessionName);
|
|
110779
|
+
this.subscribeToServer(null, sessionJoinedCallback);
|
|
110780
|
+
this.serverConnection$.next({
|
|
110781
|
+
op: "join",
|
|
110782
|
+
key: key2
|
|
110783
|
+
});
|
|
110784
|
+
};
|
|
110785
|
+
Niivue.prototype.closeSession = function() {
|
|
110786
|
+
this.interval$.complete();
|
|
110787
|
+
this.serverConnection$.complete();
|
|
110788
|
+
this.isInSession = false;
|
|
110789
|
+
this.isController = false;
|
|
110790
|
+
};
|
|
109905
110791
|
Niivue.prototype.arrayEquals = function(a, b) {
|
|
109906
110792
|
return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((val, index) => val === b[index]);
|
|
109907
110793
|
};
|
|
@@ -109956,13 +110842,13 @@ Niivue.prototype.mouseRightButtonHandler = function(e) {
|
|
|
109956
110842
|
this.dragClipPlaneStartDepthAziElev = this.scene.clipPlaneDepthAziElev;
|
|
109957
110843
|
return;
|
|
109958
110844
|
};
|
|
109959
|
-
Niivue.prototype.calculateMinMaxVoxIdx = function(
|
|
109960
|
-
if (
|
|
110845
|
+
Niivue.prototype.calculateMinMaxVoxIdx = function(array2) {
|
|
110846
|
+
if (array2.length > 2) {
|
|
109961
110847
|
throw new Error("array must not contain more than two values");
|
|
109962
110848
|
}
|
|
109963
110849
|
return [
|
|
109964
|
-
Math.floor(Math.min(
|
|
109965
|
-
Math.floor(Math.max(
|
|
110850
|
+
Math.floor(Math.min(array2[0], array2[1])),
|
|
110851
|
+
Math.floor(Math.max(array2[0], array2[1]))
|
|
109966
110852
|
];
|
|
109967
110853
|
};
|
|
109968
110854
|
function intensityRaw2Scaled(hdr, raw) {
|
|
@@ -109971,14 +110857,18 @@ function intensityRaw2Scaled(hdr, raw) {
|
|
|
109971
110857
|
return raw * hdr.scl_slope + hdr.scl_inter;
|
|
109972
110858
|
}
|
|
109973
110859
|
Niivue.prototype.calculateNewRange = function(volIdx = 0) {
|
|
109974
|
-
if (this.sliceType === this.sliceTypeRender) {
|
|
110860
|
+
if (this.sliceType === this.sliceTypeRender && this.sliceMosaicString.length < 1) {
|
|
109975
110861
|
return;
|
|
109976
110862
|
}
|
|
109977
110863
|
if (this.dragStart[0] === this.dragEnd[0] && this.dragStart[1] === this.dragEnd[1])
|
|
109978
110864
|
return;
|
|
109979
110865
|
let frac = this.canvasPos2frac([this.dragStart[0], this.dragStart[1]]);
|
|
110866
|
+
if (frac[0] < 0)
|
|
110867
|
+
return;
|
|
109980
110868
|
let startVox = this.frac2vox(frac, volIdx);
|
|
109981
110869
|
frac = this.canvasPos2frac([this.dragEnd[0], this.dragEnd[1]]);
|
|
110870
|
+
if (frac[0] < 0)
|
|
110871
|
+
return;
|
|
109982
110872
|
let endVox = this.frac2vox(frac, volIdx);
|
|
109983
110873
|
let hi = -Number.MAX_VALUE, lo = Number.MAX_VALUE;
|
|
109984
110874
|
let xrange;
|
|
@@ -110019,7 +110909,7 @@ Niivue.prototype.calculateNewRange = function(volIdx = 0) {
|
|
|
110019
110909
|
var mxScale = intensityRaw2Scaled(hdr, hi);
|
|
110020
110910
|
this.volumes[volIdx].cal_min = mnScale;
|
|
110021
110911
|
this.volumes[volIdx].cal_max = mxScale;
|
|
110022
|
-
this.
|
|
110912
|
+
this.opts.onIntensityChange(this.volumes[volIdx]);
|
|
110023
110913
|
};
|
|
110024
110914
|
Niivue.prototype.mouseUpListener = function() {
|
|
110025
110915
|
this.scene.mousedown = false;
|
|
@@ -110027,10 +110917,14 @@ Niivue.prototype.mouseUpListener = function() {
|
|
|
110027
110917
|
this.scene.mouseButtonLeftDown = false;
|
|
110028
110918
|
if (this.drawPenFillPts.length > 0)
|
|
110029
110919
|
this.drawPenFilled();
|
|
110920
|
+
else if (this.drawPenAxCorSag >= 0)
|
|
110921
|
+
this.drawAddUndoBitmap();
|
|
110030
110922
|
this.drawPenLocation = [NaN, NaN, NaN];
|
|
110031
110923
|
this.drawPenAxCorSag = -1;
|
|
110032
110924
|
if (this.isDragging) {
|
|
110033
110925
|
this.isDragging = false;
|
|
110926
|
+
if (this.opts.isDragShowsMeasurementTool)
|
|
110927
|
+
return;
|
|
110034
110928
|
this.calculateNewRange();
|
|
110035
110929
|
this.refreshLayers(this.volumes[0], 0, this.volumes.length);
|
|
110036
110930
|
}
|
|
@@ -110073,15 +110967,24 @@ Niivue.prototype.mouseMoveListener = function(e) {
|
|
|
110073
110967
|
this.scene.prevY = this.scene.currY;
|
|
110074
110968
|
}
|
|
110075
110969
|
};
|
|
110076
|
-
Niivue.prototype.resetBriCon = function() {
|
|
110970
|
+
Niivue.prototype.resetBriCon = function(msg2 = null) {
|
|
110971
|
+
let isRender = false;
|
|
110077
110972
|
if (this.sliceType === this.sliceTypeRender) {
|
|
110973
|
+
isRender = true;
|
|
110974
|
+
}
|
|
110975
|
+
if (msg2 !== null) {
|
|
110976
|
+
if (this.inRenderTile(msg2.offsetX, msg2.offsetY) >= 0)
|
|
110977
|
+
isRender = true;
|
|
110978
|
+
}
|
|
110979
|
+
if (isRender) {
|
|
110078
110980
|
this.scene.mouseDepthPicker = true;
|
|
110079
110981
|
this.drawScene();
|
|
110982
|
+
this.drawScene();
|
|
110080
110983
|
return;
|
|
110081
110984
|
}
|
|
110082
110985
|
this.volumes[0].cal_min = this.volumes[0].robust_min;
|
|
110083
110986
|
this.volumes[0].cal_max = this.volumes[0].robust_max;
|
|
110084
|
-
this.
|
|
110987
|
+
this.opts.onIntensityChange(this.volumes[0]);
|
|
110085
110988
|
this.refreshLayers(this.volumes[0], 0, this.volumes.length);
|
|
110086
110989
|
this.drawScene();
|
|
110087
110990
|
};
|
|
@@ -110112,9 +111015,6 @@ Niivue.prototype.handlePinchZoom = function(e) {
|
|
|
110112
111015
|
};
|
|
110113
111016
|
Niivue.prototype.keyUpListener = function(e) {
|
|
110114
111017
|
if (e.code === this.opts.clipPlaneHotKey) {
|
|
110115
|
-
if (this.sliceType != this.sliceTypeRender) {
|
|
110116
|
-
return;
|
|
110117
|
-
}
|
|
110118
111018
|
let now = new Date().getTime();
|
|
110119
111019
|
let elapsed = now - this.lastCalled;
|
|
110120
111020
|
if (elapsed > this.opts.keyDebounceTime) {
|
|
@@ -110312,8 +111212,26 @@ Niivue.prototype.dropListener = async function(e) {
|
|
|
110312
111212
|
}
|
|
110313
111213
|
this.drawScene();
|
|
110314
111214
|
};
|
|
111215
|
+
Niivue.prototype.setCornerOrientationText = function(isCornerOrientationText) {
|
|
111216
|
+
this.opts.isCornerOrientationText = isCornerOrientationText;
|
|
111217
|
+
this.updateGLVolume();
|
|
111218
|
+
};
|
|
110315
111219
|
Niivue.prototype.setRadiologicalConvention = function(isRadiologicalConvention) {
|
|
110316
111220
|
this.opts.isRadiologicalConvention = isRadiologicalConvention;
|
|
111221
|
+
this.updateGLVolume();
|
|
111222
|
+
};
|
|
111223
|
+
Niivue.prototype.setMeshThicknessOn2D = function(meshThicknessOn2D) {
|
|
111224
|
+
this.opts.meshThicknessOn2D = meshThicknessOn2D;
|
|
111225
|
+
this.updateGLVolume();
|
|
111226
|
+
};
|
|
111227
|
+
Niivue.prototype.setSliceMosaicString = function(str) {
|
|
111228
|
+
this.sliceMosaicString = str;
|
|
111229
|
+
this.updateGLVolume();
|
|
111230
|
+
};
|
|
111231
|
+
Niivue.prototype.setSliceMM = function(isSliceMM) {
|
|
111232
|
+
this.opts.isSliceMM = isSliceMM;
|
|
111233
|
+
console.log("SliceMM feature is experimental");
|
|
111234
|
+
this.updateGLVolume();
|
|
110317
111235
|
};
|
|
110318
111236
|
Niivue.prototype.getRadiologicalConvention = function() {
|
|
110319
111237
|
return this.opts.isRadiologicalConvention;
|
|
@@ -110322,13 +111240,13 @@ Niivue.prototype.addVolume = function(volume) {
|
|
|
110322
111240
|
this.volumes.push(volume);
|
|
110323
111241
|
let idx = this.volumes.length === 1 ? 0 : this.volumes.length - 1;
|
|
110324
111242
|
this.setVolume(volume, idx);
|
|
110325
|
-
this.
|
|
111243
|
+
this.opts.onImageLoaded(volume);
|
|
110326
111244
|
};
|
|
110327
111245
|
Niivue.prototype.addMesh = function(mesh) {
|
|
110328
111246
|
this.meshes.push(mesh);
|
|
110329
111247
|
let idx = this.meshes.length === 1 ? 0 : this.meshes.length - 1;
|
|
110330
111248
|
this.setMesh(mesh, idx);
|
|
110331
|
-
this.
|
|
111249
|
+
this.opts.onImageLoaded(mesh);
|
|
110332
111250
|
};
|
|
110333
111251
|
Niivue.prototype.getVolumeIndexByID = function(id) {
|
|
110334
111252
|
let n = this.volumes.length;
|
|
@@ -110340,9 +111258,112 @@ Niivue.prototype.getVolumeIndexByID = function(id) {
|
|
|
110340
111258
|
}
|
|
110341
111259
|
return -1;
|
|
110342
111260
|
};
|
|
111261
|
+
function encodeRLE(data) {
|
|
111262
|
+
let dl = data.length;
|
|
111263
|
+
let dp = 0;
|
|
111264
|
+
let r = new Uint8Array(dl + Math.ceil(0.01 * dl));
|
|
111265
|
+
let rI = new Int8Array(r.buffer);
|
|
111266
|
+
let rp = 0;
|
|
111267
|
+
while (dp < dl) {
|
|
111268
|
+
let v = data[dp];
|
|
111269
|
+
dp++;
|
|
111270
|
+
let rl = 1;
|
|
111271
|
+
while (rl < 129 && dp < dl && data[dp] === v) {
|
|
111272
|
+
dp++;
|
|
111273
|
+
rl++;
|
|
111274
|
+
}
|
|
111275
|
+
if (rl > 1) {
|
|
111276
|
+
rI[rp] = -rl + 1;
|
|
111277
|
+
rp++;
|
|
111278
|
+
r[rp] = v;
|
|
111279
|
+
rp++;
|
|
111280
|
+
continue;
|
|
111281
|
+
}
|
|
111282
|
+
while (dp < dl) {
|
|
111283
|
+
if (rl > 127)
|
|
111284
|
+
break;
|
|
111285
|
+
if (dp + 2 < dl) {
|
|
111286
|
+
if (v !== data[dp] && data[dp + 2] === data[dp] && data[dp + 1] === data[dp])
|
|
111287
|
+
break;
|
|
111288
|
+
}
|
|
111289
|
+
v = data[dp];
|
|
111290
|
+
dp++;
|
|
111291
|
+
rl++;
|
|
111292
|
+
}
|
|
111293
|
+
r[rp] = rl - 1;
|
|
111294
|
+
rp++;
|
|
111295
|
+
for (let i2 = 0; i2 < rl; i2++) {
|
|
111296
|
+
r[rp] = data[dp - rl + i2];
|
|
111297
|
+
rp++;
|
|
111298
|
+
}
|
|
111299
|
+
}
|
|
111300
|
+
log.info("PackBits " + dl + " -> " + rp + " bytes (x" + dl / rp + ")");
|
|
111301
|
+
return r.slice(0, rp);
|
|
111302
|
+
}
|
|
111303
|
+
function decodeRLE(rle2, decodedlen) {
|
|
111304
|
+
let r = new Uint8Array(rle2.buffer);
|
|
111305
|
+
let rI = new Int8Array(r.buffer);
|
|
111306
|
+
let rp = 0;
|
|
111307
|
+
let d = new Uint8Array(decodedlen);
|
|
111308
|
+
let dp = 0;
|
|
111309
|
+
while (rp < r.length) {
|
|
111310
|
+
let hdr = rI[rp];
|
|
111311
|
+
rp++;
|
|
111312
|
+
if (hdr < 0) {
|
|
111313
|
+
let v = rI[rp];
|
|
111314
|
+
rp++;
|
|
111315
|
+
for (let i2 = 0; i2 < 1 - hdr; i2++) {
|
|
111316
|
+
d[dp] = v;
|
|
111317
|
+
dp++;
|
|
111318
|
+
}
|
|
111319
|
+
} else {
|
|
111320
|
+
for (let i2 = 0; i2 < hdr + 1; i2++) {
|
|
111321
|
+
d[dp] = rI[rp];
|
|
111322
|
+
rp++;
|
|
111323
|
+
dp++;
|
|
111324
|
+
}
|
|
111325
|
+
}
|
|
111326
|
+
}
|
|
111327
|
+
return d;
|
|
111328
|
+
}
|
|
111329
|
+
Niivue.prototype.drawAddUndoBitmap = async function(fnm) {
|
|
111330
|
+
if (!this.drawBitmap || this.drawBitmap.length < 1) {
|
|
111331
|
+
console.log("drawAddUndoBitmap error: No drawing open");
|
|
111332
|
+
return false;
|
|
111333
|
+
}
|
|
111334
|
+
this.currentDrawUndoBitmap++;
|
|
111335
|
+
if (this.currentDrawUndoBitmap >= this.maxDrawUndoBitmaps)
|
|
111336
|
+
this.currentDrawUndoBitmap = 0;
|
|
111337
|
+
this.drawUndoBitmaps[this.currentDrawUndoBitmap] = encodeRLE(this.drawBitmap);
|
|
111338
|
+
};
|
|
111339
|
+
Niivue.prototype.drawClearAllUndoBitmaps = async function() {
|
|
111340
|
+
this.currentDrawUndoBitmap = this.maxDrawUndoBitmaps;
|
|
111341
|
+
if (this.drawUndoBitmaps.length < 1)
|
|
111342
|
+
return;
|
|
111343
|
+
for (let i2 = this.drawUndoBitmaps.length - 1; i2 >= 0; i2--)
|
|
111344
|
+
array[i2] = [];
|
|
111345
|
+
};
|
|
111346
|
+
Niivue.prototype.drawUndo = function() {
|
|
111347
|
+
if (this.drawUndoBitmaps.length < 1) {
|
|
111348
|
+
console.log("undo bitmaps not loaded");
|
|
111349
|
+
return;
|
|
111350
|
+
}
|
|
111351
|
+
this.currentDrawUndoBitmap--;
|
|
111352
|
+
if (this.currentDrawUndoBitmap < 0)
|
|
111353
|
+
this.currentDrawUndoBitmap = this.drawUndoBitmaps.length - 1;
|
|
111354
|
+
if (this.currentDrawUndoBitmap >= this.drawUndoBitmaps.length)
|
|
111355
|
+
this.currentDrawUndoBitmap = 0;
|
|
111356
|
+
if (this.drawUndoBitmaps[this.currentDrawUndoBitmap].length < 2) {
|
|
111357
|
+
console.log("drawUndo is misbehaving");
|
|
111358
|
+
return;
|
|
111359
|
+
}
|
|
111360
|
+
this.drawBitmap = decodeRLE(this.drawUndoBitmaps[this.currentDrawUndoBitmap], this.drawBitmap.length);
|
|
111361
|
+
this.refreshDrawing(true);
|
|
111362
|
+
};
|
|
110343
111363
|
Niivue.prototype.loadDrawing = async function(fnm) {
|
|
110344
111364
|
if (this.drawBitmap)
|
|
110345
111365
|
console.log("Overwriting open drawing!");
|
|
111366
|
+
this.drawClearAllUndoBitmaps();
|
|
110346
111367
|
let volume = await NVImage.loadFromUrl({ url: fnm });
|
|
110347
111368
|
let dims = volume.hdr.dims;
|
|
110348
111369
|
if (dims[1] !== this.back.hdr.dims[1] || dims[2] !== this.back.hdr.dims[2] || dims[3] !== this.back.hdr.dims[3]) {
|
|
@@ -110353,7 +111374,6 @@ Niivue.prototype.loadDrawing = async function(fnm) {
|
|
|
110353
111374
|
console.log("Drawings should be UINT8");
|
|
110354
111375
|
let perm = volume.permRAS;
|
|
110355
111376
|
let vx = dims[1] * dims[2] * dims[3];
|
|
110356
|
-
this.drawUndoBitmap = null;
|
|
110357
111377
|
this.drawBitmap = new Uint8Array(vx);
|
|
110358
111378
|
this.drawTexture = this.r8Tex(this.drawTexture, this.gl.TEXTURE7, this.back.dims, true);
|
|
110359
111379
|
let layout = [0, 0, 0];
|
|
@@ -110405,6 +111425,7 @@ Niivue.prototype.loadDrawing = async function(fnm) {
|
|
|
110405
111425
|
}
|
|
110406
111426
|
}
|
|
110407
111427
|
}
|
|
111428
|
+
this.drawAddUndoBitmap();
|
|
110408
111429
|
this.refreshDrawing(false);
|
|
110409
111430
|
this.drawScene();
|
|
110410
111431
|
};
|
|
@@ -110517,9 +111538,12 @@ Niivue.prototype.setMeshLayerProperty = function(mesh, layer, key2, val) {
|
|
|
110517
111538
|
this.meshes[idx].setLayerProperty(layer, key2, val, this.gl);
|
|
110518
111539
|
this.updateGLVolume();
|
|
110519
111540
|
};
|
|
111541
|
+
Niivue.prototype.setPan2Dxyzmm = function(xyzmmZoom) {
|
|
111542
|
+
this.scene.pan2Dxyzmm = xyzmmZoom;
|
|
111543
|
+
this.drawScene();
|
|
111544
|
+
};
|
|
110520
111545
|
Niivue.prototype.setScale2D = function(zoomFactor) {
|
|
110521
111546
|
this.scene.scale2D = zoomFactor;
|
|
110522
|
-
console.log("bingo");
|
|
110523
111547
|
this.drawScene();
|
|
110524
111548
|
};
|
|
110525
111549
|
Niivue.prototype.setRenderAzimuthElevation = function(a, e) {
|
|
@@ -110614,12 +111638,12 @@ Niivue.prototype.moveVolumeToTop = function(volume) {
|
|
|
110614
111638
|
this.setVolume(volume, this.volumes.length - 1);
|
|
110615
111639
|
};
|
|
110616
111640
|
Niivue.prototype.mouseDown = function mouseDown(x2, y) {
|
|
110617
|
-
if (this.
|
|
111641
|
+
if (this.inRenderTile(x2, y) < 0)
|
|
110618
111642
|
return;
|
|
110619
111643
|
this.mousePos = [x2, y];
|
|
110620
111644
|
};
|
|
110621
111645
|
Niivue.prototype.mouseMove = function mouseMove(x2, y) {
|
|
110622
|
-
if (this.
|
|
111646
|
+
if (this.inRenderTile(x2, y) < 0)
|
|
110623
111647
|
return;
|
|
110624
111648
|
this.scene.renderAzimuth += x2 - this.mousePos[0];
|
|
110625
111649
|
this.scene.renderElevation += y - this.mousePos[1];
|
|
@@ -110646,8 +111670,6 @@ Niivue.prototype.setClipPlane = function(depthAzimuthElevation) {
|
|
|
110646
111670
|
let v = this.sph2cartDeg(depthAzimuthElevation[1] + 180, depthAzimuthElevation[2]);
|
|
110647
111671
|
this.scene.clipPlane = [v[0], v[1], v[2], depthAzimuthElevation[0]];
|
|
110648
111672
|
this.scene.clipPlaneDepthAziElev = depthAzimuthElevation;
|
|
110649
|
-
if (this.sliceType != this.sliceTypeRender)
|
|
110650
|
-
return;
|
|
110651
111673
|
this.drawScene();
|
|
110652
111674
|
};
|
|
110653
111675
|
Niivue.prototype.setCrosshairColor = function(color) {
|
|
@@ -110698,6 +111720,8 @@ Niivue.prototype.setScale = function(scale2) {
|
|
|
110698
111720
|
};
|
|
110699
111721
|
Niivue.prototype.setClipPlaneColor = function(color) {
|
|
110700
111722
|
this.opts.clipPlaneColor = color;
|
|
111723
|
+
this.renderShader.use(this.gl);
|
|
111724
|
+
this.gl.uniform4fv(this.renderShader.uniforms["clipPlaneColor"], this.opts.clipPlaneColor);
|
|
110701
111725
|
this.drawScene();
|
|
110702
111726
|
};
|
|
110703
111727
|
Niivue.prototype.overlayRGBA = function(volume) {
|
|
@@ -110762,6 +111786,7 @@ Niivue.prototype.loadVolumes = async function(volumeList) {
|
|
|
110762
111786
|
url: volumeList[i2].url,
|
|
110763
111787
|
name: volumeList[i2].name,
|
|
110764
111788
|
colorMap: volumeList[i2].colorMap,
|
|
111789
|
+
colorMapNegative: volumeList[i2].colorMapNegative,
|
|
110765
111790
|
opacity: volumeList[i2].opacity,
|
|
110766
111791
|
urlImgData: volumeList[i2].urlImgData,
|
|
110767
111792
|
cal_min: volumeList[i2].cal_min,
|
|
@@ -110841,7 +111866,8 @@ Niivue.prototype.createEmptyDrawing = function() {
|
|
|
110841
111866
|
return;
|
|
110842
111867
|
let vx = this.back.dims[1] * this.back.dims[2] * this.back.dims[3];
|
|
110843
111868
|
this.drawBitmap = new Uint8Array(vx);
|
|
110844
|
-
this.
|
|
111869
|
+
this.drawClearAllUndoBitmaps();
|
|
111870
|
+
this.drawAddUndoBitmap();
|
|
110845
111871
|
this.drawTexture = this.r8Tex(this.drawTexture, this.gl.TEXTURE7, this.back.dims, true);
|
|
110846
111872
|
this.refreshDrawing(false);
|
|
110847
111873
|
};
|
|
@@ -110918,16 +111944,6 @@ function img2ras16(volume) {
|
|
|
110918
111944
|
}
|
|
110919
111945
|
return img16;
|
|
110920
111946
|
}
|
|
110921
|
-
Niivue.prototype.drawUndo = function() {
|
|
110922
|
-
let hdr = this.back.hdr;
|
|
110923
|
-
let nv = hdr.dims[1] * hdr.dims[2] * hdr.dims[3];
|
|
110924
|
-
if (this.drawUndoBitmap.length !== nv || this.drawBitmap.length !== nv) {
|
|
110925
|
-
console.log("bitmap dims are wrong");
|
|
110926
|
-
return;
|
|
110927
|
-
}
|
|
110928
|
-
this.drawBitmap = this.drawUndoBitmap.slice();
|
|
110929
|
-
this.refreshDrawing(true);
|
|
110930
|
-
};
|
|
110931
111947
|
Niivue.prototype.drawGrowCut = function() {
|
|
110932
111948
|
let hdr = this.back.hdr;
|
|
110933
111949
|
let nv = hdr.dims[1] * hdr.dims[2] * hdr.dims[3];
|
|
@@ -110935,7 +111951,6 @@ Niivue.prototype.drawGrowCut = function() {
|
|
|
110935
111951
|
console.log("bitmap dims are wrong");
|
|
110936
111952
|
return;
|
|
110937
111953
|
}
|
|
110938
|
-
this.drawUndoBitmap = this.drawBitmap.slice();
|
|
110939
111954
|
let gl = this.gl;
|
|
110940
111955
|
let fb = gl.createFramebuffer();
|
|
110941
111956
|
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
|
|
@@ -110948,16 +111963,19 @@ Niivue.prototype.drawGrowCut = function() {
|
|
|
110948
111963
|
img16[i2] = this.drawBitmap[i2];
|
|
110949
111964
|
let label0 = this.r16Tex(null, gl.TEXTURE6, this.back.dims, img16);
|
|
110950
111965
|
let label1 = this.r16Tex(null, gl.TEXTURE7, this.back.dims, img16);
|
|
111966
|
+
let kMAX_STRENGTH = 1e4;
|
|
110951
111967
|
for (let i2 = 1; i2 < nv; i2++)
|
|
110952
111968
|
if (img16[i2] > 0)
|
|
110953
|
-
img16[i2] =
|
|
111969
|
+
img16[i2] = kMAX_STRENGTH;
|
|
110954
111970
|
let strength0 = this.r16Tex(null, gl.TEXTURE4, this.back.dims, img16);
|
|
110955
111971
|
let strength1 = this.r16Tex(null, gl.TEXTURE5, this.back.dims, img16);
|
|
110956
111972
|
this.gl.bindVertexArray(this.genericVAO);
|
|
110957
111973
|
let shader = this.growCutShader;
|
|
110958
111974
|
shader.use(gl);
|
|
111975
|
+
let iterations = 128;
|
|
111976
|
+
gl.uniform1i(shader.uniforms["finalPass"], 0);
|
|
110959
111977
|
gl.uniform1i(shader.uniforms["inputTexture0"], 3);
|
|
110960
|
-
for (let j = 0; j <
|
|
111978
|
+
for (let j = 0; j < iterations; j++) {
|
|
110961
111979
|
gl.uniform1i(shader.uniforms["inputTexture1"], 6);
|
|
110962
111980
|
gl.uniform1i(shader.uniforms["inputTexture2"], 4);
|
|
110963
111981
|
for (let i2 = 0; i2 < this.back.dims[3]; i2++) {
|
|
@@ -110971,6 +111989,8 @@ Niivue.prototype.drawGrowCut = function() {
|
|
|
110971
111989
|
console.error("Incomplete framebuffer");
|
|
110972
111990
|
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
110973
111991
|
}
|
|
111992
|
+
if (j === iterations - 1)
|
|
111993
|
+
gl.uniform1i(shader.uniforms["finalPass"], 1);
|
|
110974
111994
|
gl.uniform1i(shader.uniforms["inputTexture1"], 7);
|
|
110975
111995
|
gl.uniform1i(shader.uniforms["inputTexture2"], 5);
|
|
110976
111996
|
for (let i2 = 0; i2 < this.back.dims[3]; i2++) {
|
|
@@ -110986,8 +112006,8 @@ Niivue.prototype.drawGrowCut = function() {
|
|
|
110986
112006
|
}
|
|
110987
112007
|
}
|
|
110988
112008
|
gl.drawBuffers([gl.COLOR_ATTACHMENT0]);
|
|
110989
|
-
let readAttach = gl.
|
|
110990
|
-
let readTex =
|
|
112009
|
+
let readAttach = gl.COLOR_ATTACHMENT1;
|
|
112010
|
+
let readTex = label0;
|
|
110991
112011
|
gl.readBuffer(readAttach);
|
|
110992
112012
|
const format = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT);
|
|
110993
112013
|
const type2 = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE);
|
|
@@ -111023,6 +112043,7 @@ Niivue.prototype.drawGrowCut = function() {
|
|
|
111023
112043
|
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
|
111024
112044
|
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
111025
112045
|
gl.deleteFramebuffer(fb);
|
|
112046
|
+
this.drawAddUndoBitmap();
|
|
111026
112047
|
this.refreshDrawing(true);
|
|
111027
112048
|
};
|
|
111028
112049
|
Niivue.prototype.drawPt = function(x2, y, z, penValue) {
|
|
@@ -111034,7 +112055,7 @@ Niivue.prototype.drawPt = function(x2, y, z, penValue) {
|
|
|
111034
112055
|
z = Math.min(Math.max(z, 0), dz - 1);
|
|
111035
112056
|
this.drawBitmap[x2 + y * dx + z * dx * dy] = penValue;
|
|
111036
112057
|
};
|
|
111037
|
-
Niivue.prototype.
|
|
112058
|
+
Niivue.prototype.drawPenLine = function(ptA, ptB, penValue) {
|
|
111038
112059
|
let dx = Math.abs(ptA[0] - ptB[0]);
|
|
111039
112060
|
let dy = Math.abs(ptA[1] - ptB[1]);
|
|
111040
112061
|
let dz = Math.abs(ptA[2] - ptB[2]);
|
|
@@ -111106,6 +112127,74 @@ Niivue.prototype.drawLine = function(ptA, ptB, penValue) {
|
|
|
111106
112127
|
}
|
|
111107
112128
|
}
|
|
111108
112129
|
};
|
|
112130
|
+
Niivue.prototype.drawFloodFill = function(seedXYZ, newColor = 0) {
|
|
112131
|
+
newColor = Math.abs(newColor);
|
|
112132
|
+
let dims = [this.back.dims[1], this.back.dims[2], this.back.dims[3]];
|
|
112133
|
+
if (seedXYZ[0] < 0 || seedXYZ[1] < 0 || seedXYZ[2] < 0)
|
|
112134
|
+
return;
|
|
112135
|
+
if (seedXYZ[0] >= dims[0] || seedXYZ[1] >= dims[1] || seedXYZ[2] >= dims[2])
|
|
112136
|
+
return;
|
|
112137
|
+
let nx = dims[0];
|
|
112138
|
+
let nxy = nx * dims[1];
|
|
112139
|
+
let nxyz = nxy * dims[2];
|
|
112140
|
+
let img = this.drawBitmap.slice();
|
|
112141
|
+
if (img.length !== nxy * dims[2])
|
|
112142
|
+
return;
|
|
112143
|
+
function xyz2vx(pt) {
|
|
112144
|
+
return pt[0] + pt[1] * nx + pt[2] * nxy;
|
|
112145
|
+
}
|
|
112146
|
+
function vx2xyz(vx) {
|
|
112147
|
+
let Z = Math.floor(vx / nxy);
|
|
112148
|
+
let Y = Math.floor((vx - Z * nxy) / nx);
|
|
112149
|
+
let X = Math.floor(vx % nx);
|
|
112150
|
+
return [X, Y, Z];
|
|
112151
|
+
}
|
|
112152
|
+
let seedVx = xyz2vx(seedXYZ);
|
|
112153
|
+
let seedColor = img[seedVx];
|
|
112154
|
+
if (seedColor === newColor) {
|
|
112155
|
+
console.log("drawFloodFill selected voxel is already desired color");
|
|
112156
|
+
return;
|
|
112157
|
+
}
|
|
112158
|
+
for (let i2 = 1; i2 < nxyz; i2++) {
|
|
112159
|
+
img[i2] = 0;
|
|
112160
|
+
if (this.drawBitmap[i2] === seedColor)
|
|
112161
|
+
img[i2] = 1;
|
|
112162
|
+
}
|
|
112163
|
+
let Q = [];
|
|
112164
|
+
Q.push(seedVx);
|
|
112165
|
+
img[seedVx] = 2;
|
|
112166
|
+
while (Q.length > 0) {
|
|
112167
|
+
let testNeighbor = function(offset) {
|
|
112168
|
+
let xyzN = xyz.slice();
|
|
112169
|
+
xyzN[0] += offset[0];
|
|
112170
|
+
xyzN[1] += offset[1];
|
|
112171
|
+
xyzN[2] += offset[2];
|
|
112172
|
+
if (xyzN[0] < 0 || xyzN[1] < 0 || xyzN[2] < 0)
|
|
112173
|
+
return;
|
|
112174
|
+
if (xyzN[0] >= dims[0] || xyzN[1] >= dims[1] || xyzN[2] >= dims[2])
|
|
112175
|
+
return;
|
|
112176
|
+
let vxT = xyz2vx(xyzN);
|
|
112177
|
+
if (img[vxT] !== 1)
|
|
112178
|
+
return;
|
|
112179
|
+
img[vxT] = 2;
|
|
112180
|
+
Q.push(vxT);
|
|
112181
|
+
};
|
|
112182
|
+
let vx = Q[0];
|
|
112183
|
+
Q.shift();
|
|
112184
|
+
let xyz = vx2xyz(vx);
|
|
112185
|
+
testNeighbor([0, 0, -1]);
|
|
112186
|
+
testNeighbor([0, 0, 1]);
|
|
112187
|
+
testNeighbor([0, -1, 0]);
|
|
112188
|
+
testNeighbor([0, 1, 0]);
|
|
112189
|
+
testNeighbor([-1, 0, 0]);
|
|
112190
|
+
testNeighbor([1, 0, 0]);
|
|
112191
|
+
}
|
|
112192
|
+
for (let i2 = 1; i2 < nxyz; i2++)
|
|
112193
|
+
if (img[i2] === 2)
|
|
112194
|
+
this.drawBitmap[i2] = newColor;
|
|
112195
|
+
this.drawAddUndoBitmap();
|
|
112196
|
+
this.refreshDrawing(false);
|
|
112197
|
+
};
|
|
111109
112198
|
Niivue.prototype.drawPenFilled = function() {
|
|
111110
112199
|
let nPts = this.drawPenFillPts.length;
|
|
111111
112200
|
if (nPts < 2) {
|
|
@@ -111221,15 +112310,17 @@ Niivue.prototype.drawPenFilled = function() {
|
|
|
111221
112310
|
}
|
|
111222
112311
|
}
|
|
111223
112312
|
}
|
|
111224
|
-
if (!this.drawFillOverwrites && this.
|
|
111225
|
-
let nv = this.
|
|
112313
|
+
if (!this.drawFillOverwrites && this.drawUndoBitmaps[this.currentDrawUndoBitmap].length > 0) {
|
|
112314
|
+
let nv = this.drawBitmap.length;
|
|
112315
|
+
let bmp = decodeRLE(this.drawUndoBitmaps[this.currentDrawUndoBitmap], nv);
|
|
111226
112316
|
for (let i2 = 0; i2 < nv; i2++) {
|
|
111227
|
-
if (
|
|
112317
|
+
if (bmp[i2] === 0)
|
|
111228
112318
|
continue;
|
|
111229
|
-
this.drawBitmap[i2] =
|
|
112319
|
+
this.drawBitmap[i2] = bmp[i2];
|
|
111230
112320
|
}
|
|
111231
112321
|
}
|
|
111232
112322
|
this.drawPenFillPts = [];
|
|
112323
|
+
this.drawAddUndoBitmap();
|
|
111233
112324
|
this.refreshDrawing(false);
|
|
111234
112325
|
};
|
|
111235
112326
|
Niivue.prototype.closeDrawing = function() {
|
|
@@ -111473,8 +112564,15 @@ Niivue.prototype.init = async function() {
|
|
|
111473
112564
|
this.gl.uniform1i(this.sliceShader.uniforms["overlay"], 2);
|
|
111474
112565
|
this.gl.uniform1i(this.sliceShader.uniforms["drawing"], 7);
|
|
111475
112566
|
this.gl.uniform1f(this.sliceShader.uniforms["drawOpacity"], this.drawOpacity);
|
|
111476
|
-
this.
|
|
111477
|
-
this.
|
|
112567
|
+
this.sliceMMShader = new Shader(this.gl, vertSliceMMShader, fragSliceMMShader);
|
|
112568
|
+
this.sliceMMShader.use(this.gl);
|
|
112569
|
+
this.gl.uniform1i(this.sliceMMShader.uniforms["volume"], 0);
|
|
112570
|
+
this.gl.uniform1i(this.sliceMMShader.uniforms["overlay"], 2);
|
|
112571
|
+
this.gl.uniform1i(this.sliceMMShader.uniforms["drawing"], 7);
|
|
112572
|
+
this.gl.uniform1f(this.sliceMMShader.uniforms["drawOpacity"], this.drawOpacity);
|
|
112573
|
+
this.rectShader = new Shader(this.gl, vertRectShader, fragRectShader);
|
|
112574
|
+
this.lineShader = new Shader(this.gl, vertLineShader, fragRectShader);
|
|
112575
|
+
this.graphShader = new Shader(this.gl, vertGraphShader, fragRectShader);
|
|
111478
112576
|
this.renderShader = new Shader(this.gl, vertRenderShader, fragRenderShader);
|
|
111479
112577
|
this.renderShader.use(this.gl);
|
|
111480
112578
|
this.gl.uniform1i(this.renderShader.uniforms["volume"], 0);
|
|
@@ -111602,7 +112700,6 @@ Niivue.prototype.refreshLayers = function(overlayItem, layer, numLayers) {
|
|
|
111602
112700
|
if (layer === 0) {
|
|
111603
112701
|
this.volumeObject3D = overlayItem.toNiivueObject3D(this.VOLUME_ID, this.gl);
|
|
111604
112702
|
invert(mtx, mtx);
|
|
111605
|
-
log.debug(`mtx layer ${layer}`, mtx);
|
|
111606
112703
|
this.back.matRAS = overlayItem.matRAS;
|
|
111607
112704
|
this.back.dims = overlayItem.dimsRAS;
|
|
111608
112705
|
this.back.pixDims = overlayItem.pixDimsRAS;
|
|
@@ -111719,10 +112816,54 @@ Niivue.prototype.refreshLayers = function(overlayItem, layer, numLayers) {
|
|
|
111719
112816
|
this.gl.uniform1i(orientShader.uniforms["blend3D"], 5);
|
|
111720
112817
|
this.gl.uniform1i(orientShader.uniforms["colormap"], 1);
|
|
111721
112818
|
this.gl.uniform1f(orientShader.uniforms["layer"], layer);
|
|
111722
|
-
this.gl.uniform1f(orientShader.uniforms["numLayers"], numLayers);
|
|
111723
112819
|
this.gl.uniform1f(orientShader.uniforms["scl_inter"], hdr.scl_inter);
|
|
111724
112820
|
this.gl.uniform1f(orientShader.uniforms["scl_slope"], hdr.scl_slope);
|
|
111725
112821
|
this.gl.uniform1f(orientShader.uniforms["opacity"], opacity);
|
|
112822
|
+
this.gl.uniform1i(orientShader.uniforms["modulationVol"], 7);
|
|
112823
|
+
let modulateTexture = null;
|
|
112824
|
+
if (overlayItem.modulationImage && overlayItem.modulationImage >= 0 && overlayItem.modulationImage < this.volumes.length) {
|
|
112825
|
+
console.log(this.volumes);
|
|
112826
|
+
let mhdr = this.volumes[overlayItem.modulationImage].hdr;
|
|
112827
|
+
if (mhdr.dims[1] === hdr.dims[1] && mhdr.dims[2] === hdr.dims[2] && mhdr.dims[3] === hdr.dims[3]) {
|
|
112828
|
+
this.gl.uniform1i(orientShader.uniforms["modulation"], 1);
|
|
112829
|
+
modulateTexture = this.r8Tex(modulateTexture, this.gl.TEXTURE7, hdr.dims, true);
|
|
112830
|
+
this.gl.activeTexture(this.gl.TEXTURE7);
|
|
112831
|
+
this.gl.bindTexture(this.gl.TEXTURE_3D, modulateTexture);
|
|
112832
|
+
let vx = hdr.dims[1] * hdr.dims[2] * hdr.dims[3];
|
|
112833
|
+
let modulateVolume = new Uint8Array(vx);
|
|
112834
|
+
let mn = mhdr.cal_min;
|
|
112835
|
+
let scale2 = 255 / (mhdr.cal_max - mhdr.cal_min);
|
|
112836
|
+
let imgRaw = this.volumes[overlayItem.modulationImage].img.buffer;
|
|
112837
|
+
let img2 = new Uint8Array(imgRaw);
|
|
112838
|
+
switch (mhdr.datatypeCode) {
|
|
112839
|
+
case overlayItem.DT_SIGNED_SHORT:
|
|
112840
|
+
img2 = new Int16Array(imgRaw);
|
|
112841
|
+
break;
|
|
112842
|
+
case overlayItem.DT_FLOAT:
|
|
112843
|
+
img2 = new Float32Array(imgRaw);
|
|
112844
|
+
break;
|
|
112845
|
+
case overlayItem.DT_DOUBLE:
|
|
112846
|
+
img2 = new Float64Array(imgRaw);
|
|
112847
|
+
break;
|
|
112848
|
+
case overlayItem.DT_RGB:
|
|
112849
|
+
img2 = new Uint8Array(imgRaw);
|
|
112850
|
+
break;
|
|
112851
|
+
case overlayItem.DT_UINT16:
|
|
112852
|
+
img2 = new Uint16Array(imgRaw);
|
|
112853
|
+
break;
|
|
112854
|
+
}
|
|
112855
|
+
console.log(this.volumes[overlayItem.modulationImage]);
|
|
112856
|
+
for (let i2 = 0; i2 < vx; i2++) {
|
|
112857
|
+
let v = img2[i2] * mhdr.scl_slope + mhdr.scl_inter;
|
|
112858
|
+
v = (v - mn) * scale2;
|
|
112859
|
+
v = Math.min(Math.max(v, 0), 255);
|
|
112860
|
+
modulateVolume[i2] = v;
|
|
112861
|
+
}
|
|
112862
|
+
this.gl.texSubImage3D(this.gl.TEXTURE_3D, 0, 0, 0, 0, hdr.dims[1], hdr.dims[2], hdr.dims[3], this.gl.RED, this.gl.UNSIGNED_BYTE, modulateVolume);
|
|
112863
|
+
} else
|
|
112864
|
+
console.log("Modulation image dimensions do not match target");
|
|
112865
|
+
} else
|
|
112866
|
+
this.gl.uniform1i(orientShader.uniforms["modulation"], 0);
|
|
111726
112867
|
this.gl.uniformMatrix4fv(orientShader.uniforms["mtx"], false, mtx);
|
|
111727
112868
|
if (hdr.intent_code === 1002) {
|
|
111728
112869
|
let x2 = 1 / this.back.dims[1];
|
|
@@ -111743,6 +112884,7 @@ Niivue.prototype.refreshLayers = function(overlayItem, layer, numLayers) {
|
|
|
111743
112884
|
}
|
|
111744
112885
|
this.gl.bindVertexArray(this.unusedVAO);
|
|
111745
112886
|
this.gl.deleteTexture(tempTex3D);
|
|
112887
|
+
this.gl.deleteTexture(modulateTexture);
|
|
111746
112888
|
this.gl.deleteTexture(blendTexture);
|
|
111747
112889
|
this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
|
|
111748
112890
|
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);
|
|
@@ -111760,10 +112902,16 @@ Niivue.prototype.refreshLayers = function(overlayItem, layer, numLayers) {
|
|
|
111760
112902
|
this.pickingShader.use(this.gl);
|
|
111761
112903
|
this.gl.uniform1f(this.pickingShader.uniforms["overlays"], this.overlays.length);
|
|
111762
112904
|
this.gl.uniform3fv(this.pickingShader.uniforms["texVox"], vox);
|
|
112905
|
+
this.sliceMMShader.use(this.gl);
|
|
112906
|
+
this.gl.uniform1f(this.sliceMMShader.uniforms["overlays"], this.overlays.length);
|
|
112907
|
+
this.gl.uniform1f(this.sliceMMShader.uniforms["drawOpacity"], this.drawOpacity);
|
|
112908
|
+
this.gl.uniform1i(this.sliceMMShader.uniforms["drawing"], 7);
|
|
111763
112909
|
this.sliceShader.use(this.gl);
|
|
111764
112910
|
this.gl.uniform1f(this.sliceShader.uniforms["overlays"], this.overlays.length);
|
|
111765
112911
|
this.gl.uniform1f(this.sliceShader.uniforms["drawOpacity"], this.drawOpacity);
|
|
111766
112912
|
this.gl.uniform1i(this.sliceShader.uniforms["drawing"], 7);
|
|
112913
|
+
this.gl.activeTexture(this.gl.TEXTURE7);
|
|
112914
|
+
this.gl.bindTexture(this.gl.TEXTURE_3D, this.drawTexture);
|
|
111767
112915
|
this.updateInterpolation(layer);
|
|
111768
112916
|
};
|
|
111769
112917
|
Niivue.prototype.colorMaps = function(sort = true) {
|
|
@@ -111774,6 +112922,18 @@ Niivue.prototype.setColorMap = function(id, colorMap) {
|
|
|
111774
112922
|
this.volumes[idx].colorMap = colorMap;
|
|
111775
112923
|
this.updateGLVolume();
|
|
111776
112924
|
};
|
|
112925
|
+
Niivue.prototype.setColorMapNegative = function(id, colorMapNegative) {
|
|
112926
|
+
let idx = this.getVolumeIndexByID(id);
|
|
112927
|
+
this.volumes[idx].colorMapNegative = colorMapNegative;
|
|
112928
|
+
this.updateGLVolume();
|
|
112929
|
+
};
|
|
112930
|
+
Niivue.prototype.setModulationImage = function(idTarget, idModulation) {
|
|
112931
|
+
let idxTarget = this.getVolumeIndexByID(idTarget);
|
|
112932
|
+
let idxModulation = null;
|
|
112933
|
+
idxModulation = this.getVolumeIndexByID(idModulation);
|
|
112934
|
+
this.volumes[idxTarget].modulationImage = idxModulation;
|
|
112935
|
+
this.updateGLVolume();
|
|
112936
|
+
};
|
|
111777
112937
|
Niivue.prototype.setGamma = function(gamma = 1) {
|
|
111778
112938
|
cmapper.gamma = gamma;
|
|
111779
112939
|
this.updateGLVolume();
|
|
@@ -111808,40 +112968,87 @@ Niivue.prototype.refreshColormaps = function() {
|
|
|
111808
112968
|
this.colormapTexture = this.gl.createTexture();
|
|
111809
112969
|
this.gl.activeTexture(this.gl.TEXTURE1);
|
|
111810
112970
|
this.gl.bindTexture(this.gl.TEXTURE_2D, this.colormapTexture);
|
|
111811
|
-
this.gl.texStorage2D(this.gl.TEXTURE_2D, 1, this.gl.RGBA8, 256, nLayer);
|
|
112971
|
+
this.gl.texStorage2D(this.gl.TEXTURE_2D, 1, this.gl.RGBA8, 256, nLayer * 2);
|
|
111812
112972
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
|
|
111813
112973
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
|
|
111814
112974
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_R, this.gl.CLAMP_TO_EDGE);
|
|
111815
112975
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
|
|
111816
112976
|
this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT, 1);
|
|
111817
112977
|
let luts2 = this.colormap(this.volumes[0].colorMap);
|
|
111818
|
-
|
|
111819
|
-
|
|
111820
|
-
|
|
111821
|
-
|
|
111822
|
-
|
|
111823
|
-
|
|
111824
|
-
|
|
111825
|
-
|
|
111826
|
-
|
|
111827
|
-
|
|
112978
|
+
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;
|
|
112983
|
+
}
|
|
112984
|
+
for (let i2 = 1; i2 < nLayer; i2++)
|
|
112985
|
+
addColormap(this.colormap(this.volumes[i2].colorMap));
|
|
112986
|
+
let bogusLut = new Uint8ClampedArray(1024);
|
|
112987
|
+
bogusLut[0] = 255;
|
|
112988
|
+
bogusLut[1] = 255;
|
|
112989
|
+
bogusLut[2] = 255;
|
|
112990
|
+
bogusLut[3] = 0;
|
|
112991
|
+
bogusLut[4] = 0;
|
|
112992
|
+
bogusLut[5] = 0;
|
|
112993
|
+
bogusLut[6] = 0;
|
|
112994
|
+
bogusLut[7] = 255;
|
|
112995
|
+
for (let i2 = 0; i2 < nLayer; i2++) {
|
|
112996
|
+
let lut = bogusLut.slice();
|
|
112997
|
+
if (this.volumes[i2].colorMapNegative.length > 0)
|
|
112998
|
+
lut = this.colormap(this.volumes[i2].colorMapNegative);
|
|
112999
|
+
addColormap(lut);
|
|
113000
|
+
}
|
|
113001
|
+
this.gl.texSubImage2D(this.gl.TEXTURE_2D, 0, 0, 0, 256, nLayer * 2, this.gl.RGBA, this.gl.UNSIGNED_BYTE, luts2);
|
|
111828
113002
|
return this;
|
|
111829
113003
|
};
|
|
111830
113004
|
Niivue.prototype.sliceScale = function() {
|
|
111831
|
-
|
|
111832
|
-
|
|
111833
|
-
this.back.dims[1] * this.back.pixDims[1],
|
|
111834
|
-
this.back.dims[2] * this.back.pixDims[2],
|
|
111835
|
-
this.back.dims[3] * this.back.pixDims[3]
|
|
111836
|
-
];
|
|
111837
|
-
var longestAxis = Math.max(dims[1], Math.max(dims[2], dims[3]));
|
|
113005
|
+
let dimsMM = this.screenFieldOfViewMM(this.sliceTypeAxial);
|
|
113006
|
+
var longestAxis = Math.max(dimsMM[0], Math.max(dimsMM[1], dimsMM[2]));
|
|
111838
113007
|
var volScale = [
|
|
111839
|
-
|
|
111840
|
-
|
|
111841
|
-
|
|
113008
|
+
dimsMM[0] / longestAxis,
|
|
113009
|
+
dimsMM[1] / longestAxis,
|
|
113010
|
+
dimsMM[2] / longestAxis
|
|
111842
113011
|
];
|
|
111843
113012
|
var vox = [this.back.dims[1], this.back.dims[2], this.back.dims[3]];
|
|
111844
|
-
return { volScale, vox };
|
|
113013
|
+
return { volScale, vox, longestAxis, dimsMM };
|
|
113014
|
+
};
|
|
113015
|
+
function swizzleVec3(vec, order = [0, 1, 2]) {
|
|
113016
|
+
let vout = create$1();
|
|
113017
|
+
vout[0] = vec[order[0]];
|
|
113018
|
+
vout[1] = vec[order[1]];
|
|
113019
|
+
vout[2] = vec[order[2]];
|
|
113020
|
+
return vout;
|
|
113021
|
+
}
|
|
113022
|
+
Niivue.prototype.inRenderTile = function(x2, y) {
|
|
113023
|
+
for (let i2 = 0; i2 < this.screenSlices.length; i2++) {
|
|
113024
|
+
if (this.screenSlices[i2].axCorSag !== this.sliceTypeRender)
|
|
113025
|
+
continue;
|
|
113026
|
+
let ltwh = this.screenSlices[i2].leftTopWidthHeight;
|
|
113027
|
+
if (x2 > ltwh[0] && y > ltwh[1] && x2 < ltwh[0] + ltwh[2] && y < ltwh[1] + ltwh[3])
|
|
113028
|
+
return i2;
|
|
113029
|
+
}
|
|
113030
|
+
return -1;
|
|
113031
|
+
};
|
|
113032
|
+
Niivue.prototype.sliceScroll3D = function(posChange = 0) {
|
|
113033
|
+
if (posChange === 0)
|
|
113034
|
+
return;
|
|
113035
|
+
if (this.volumes.length > 0 && this.scene.clipPlaneDepthAziElev[0] < 1.8) {
|
|
113036
|
+
let depthAziElev = this.scene.clipPlaneDepthAziElev.slice();
|
|
113037
|
+
if (posChange > 0)
|
|
113038
|
+
depthAziElev[0] = Math.min(1.5, depthAziElev[0] + 0.025);
|
|
113039
|
+
if (posChange < 0)
|
|
113040
|
+
depthAziElev[0] = Math.max(-1.5, depthAziElev[0] - 0.025);
|
|
113041
|
+
if (depthAziElev[0] !== this.scene.clipPlaneDepthAziElev[0]) {
|
|
113042
|
+
this.scene.clipPlaneDepthAziElev = depthAziElev;
|
|
113043
|
+
return this.setClipPlane(this.scene.clipPlaneDepthAziElev);
|
|
113044
|
+
}
|
|
113045
|
+
return;
|
|
113046
|
+
}
|
|
113047
|
+
if (posChange > 0)
|
|
113048
|
+
this.volScaleMultiplier = Math.min(2, this.volScaleMultiplier * 1.1);
|
|
113049
|
+
if (posChange < 0)
|
|
113050
|
+
this.volScaleMultiplier = Math.max(0.5, this.volScaleMultiplier * 0.9);
|
|
113051
|
+
this.drawScene();
|
|
111845
113052
|
};
|
|
111846
113053
|
Niivue.prototype.mouseClick = function(x2, y, posChange = 0, isDelta = true) {
|
|
111847
113054
|
var posNow;
|
|
@@ -111864,48 +113071,23 @@ Niivue.prototype.mouseClick = function(x2, y, posChange = 0, isDelta = true) {
|
|
|
111864
113071
|
return;
|
|
111865
113072
|
}
|
|
111866
113073
|
}
|
|
111867
|
-
if (this.
|
|
111868
|
-
|
|
111869
|
-
return;
|
|
111870
|
-
if (this.volumes.length > 0 && this.scene.clipPlaneDepthAziElev[0] < 1.8) {
|
|
111871
|
-
let depthAziElev = this.scene.clipPlaneDepthAziElev.slice();
|
|
111872
|
-
if (posChange > 0)
|
|
111873
|
-
depthAziElev[0] = Math.min(1.5, depthAziElev[0] + 0.025);
|
|
111874
|
-
if (posChange < 0)
|
|
111875
|
-
depthAziElev[0] = Math.max(-1.5, depthAziElev[0] - 0.025);
|
|
111876
|
-
if (depthAziElev[0] !== this.scene.clipPlaneDepthAziElev[0]) {
|
|
111877
|
-
this.scene.clipPlaneDepthAziElev = depthAziElev;
|
|
111878
|
-
return this.setClipPlane(this.scene.clipPlaneDepthAziElev);
|
|
111879
|
-
}
|
|
111880
|
-
return;
|
|
111881
|
-
}
|
|
111882
|
-
if (posChange > 0)
|
|
111883
|
-
this.volScaleMultiplier = Math.min(2, this.volScaleMultiplier * 1.1);
|
|
111884
|
-
if (posChange < 0)
|
|
111885
|
-
this.volScaleMultiplier = Math.max(0.5, this.volScaleMultiplier * 0.9);
|
|
113074
|
+
if (this.inRenderTile(x2, y) >= 0) {
|
|
113075
|
+
this.sliceScroll3D(posChange);
|
|
111886
113076
|
this.drawScene();
|
|
111887
113077
|
return;
|
|
111888
113078
|
}
|
|
111889
|
-
if (this.
|
|
113079
|
+
if (this.screenSlices.length < 1 || this.gl.canvas.height < 1 || this.gl.canvas.width < 1)
|
|
111890
113080
|
return;
|
|
111891
|
-
for (let i2 = 0; i2 < this.
|
|
113081
|
+
for (let i2 = 0; i2 < this.screenSlices.length; i2++) {
|
|
111892
113082
|
var axCorSag = this.screenSlices[i2].axCorSag;
|
|
111893
113083
|
if (this.drawPenAxCorSag >= 0 && this.drawPenAxCorSag !== axCorSag)
|
|
111894
113084
|
continue;
|
|
111895
113085
|
if (axCorSag > this.sliceTypeSagittal)
|
|
111896
113086
|
continue;
|
|
111897
|
-
|
|
111898
|
-
|
|
111899
|
-
|
|
111900
|
-
|
|
111901
|
-
ltwh[0] += ltwh[2];
|
|
111902
|
-
ltwh[2] = -ltwh[2];
|
|
111903
|
-
}
|
|
111904
|
-
var fracX = (x2 - ltwh[0]) / ltwh[2];
|
|
111905
|
-
if (isMirror)
|
|
111906
|
-
fracX = 1 - fracX;
|
|
111907
|
-
var fracY = 1 - (y - ltwh[1]) / ltwh[3];
|
|
111908
|
-
if (fracX >= 0 && fracX < 1 && fracY >= 0 && fracY < 1) {
|
|
113087
|
+
let texFrac = this.screenXY2TextureFrac(x2, y, i2);
|
|
113088
|
+
if (texFrac[0] < 0)
|
|
113089
|
+
continue;
|
|
113090
|
+
{
|
|
111909
113091
|
if (!isDelta) {
|
|
111910
113092
|
this.scene.crosshairPos[2 - axCorSag] = posChange;
|
|
111911
113093
|
this.drawScene();
|
|
@@ -111920,42 +113102,35 @@ Niivue.prototype.mouseClick = function(x2, y, posChange = 0, isDelta = true) {
|
|
|
111920
113102
|
posFuture = 0;
|
|
111921
113103
|
this.scene.crosshairPos[2 - axCorSag] = posFuture;
|
|
111922
113104
|
this.drawScene();
|
|
111923
|
-
this.
|
|
113105
|
+
this.opts.onLocationChange({
|
|
111924
113106
|
mm: this.frac2mm(this.scene.crosshairPos),
|
|
111925
113107
|
vox: this.frac2vox(this.scene.crosshairPos),
|
|
111926
113108
|
frac: this.scene.crosshairPos,
|
|
113109
|
+
xy: [x2, y],
|
|
111927
113110
|
values: this.volumes.map((v) => {
|
|
111928
113111
|
let mm = this.frac2mm(this.scene.crosshairPos);
|
|
111929
113112
|
let vox = v.mm2vox(mm);
|
|
111930
113113
|
let val = v.getValue(...vox);
|
|
111931
|
-
return val;
|
|
113114
|
+
return { name: v.name, value: val, id: v.id, mm, vox };
|
|
111932
113115
|
})
|
|
111933
113116
|
});
|
|
111934
113117
|
return;
|
|
111935
113118
|
}
|
|
111936
|
-
|
|
111937
|
-
this.scene.crosshairPos[0] = fracX;
|
|
111938
|
-
this.scene.crosshairPos[1] = fracY;
|
|
111939
|
-
}
|
|
111940
|
-
if (axCorSag === this.sliceTypeCoronal) {
|
|
111941
|
-
this.scene.crosshairPos[0] = fracX;
|
|
111942
|
-
this.scene.crosshairPos[2] = fracY;
|
|
111943
|
-
}
|
|
111944
|
-
if (axCorSag === this.sliceTypeSagittal) {
|
|
111945
|
-
this.scene.crosshairPos[1] = fracX;
|
|
111946
|
-
this.scene.crosshairPos[2] = fracY;
|
|
111947
|
-
}
|
|
113119
|
+
this.scene.crosshairPos = texFrac.slice();
|
|
111948
113120
|
if (this.opts.drawingEnabled) {
|
|
111949
113121
|
let pt = this.frac2vox(this.scene.crosshairPos);
|
|
113122
|
+
if (this.opts.penValue < 0 || Object.is(this.opts.penValue, -0)) {
|
|
113123
|
+
this.drawFloodFill(pt, Math.abs(this.opts.penValue));
|
|
113124
|
+
return;
|
|
113125
|
+
}
|
|
111950
113126
|
if (isNaN(this.drawPenLocation[0])) {
|
|
111951
113127
|
this.drawPenAxCorSag = axCorSag;
|
|
111952
113128
|
this.drawPenFillPts = [];
|
|
111953
113129
|
this.drawPt(...pt, this.opts.penValue);
|
|
111954
|
-
this.drawUndoBitmap = this.drawBitmap.slice();
|
|
111955
113130
|
} else {
|
|
111956
113131
|
if (pt[0] === this.drawPenLocation[0] && pt[1] === this.drawPenLocation[1] && pt[2] === this.drawPenLocation[2])
|
|
111957
113132
|
return;
|
|
111958
|
-
this.
|
|
113133
|
+
this.drawPenLine(pt, this.drawPenLocation, this.opts.penValue);
|
|
111959
113134
|
}
|
|
111960
113135
|
this.drawPenLocation = pt;
|
|
111961
113136
|
if (this.opts.isFilledPen)
|
|
@@ -111963,57 +113138,287 @@ Niivue.prototype.mouseClick = function(x2, y, posChange = 0, isDelta = true) {
|
|
|
111963
113138
|
this.refreshDrawing(false);
|
|
111964
113139
|
}
|
|
111965
113140
|
this.drawScene();
|
|
111966
|
-
this.
|
|
113141
|
+
this.opts.onLocationChange({
|
|
111967
113142
|
mm: this.frac2mm(this.scene.crosshairPos),
|
|
111968
113143
|
vox: this.frac2vox(this.scene.crosshairPos),
|
|
111969
113144
|
frac: this.scene.crosshairPos,
|
|
113145
|
+
xy: [x2, y],
|
|
111970
113146
|
values: this.volumes.map((v) => {
|
|
111971
113147
|
let mm = this.frac2mm(this.scene.crosshairPos);
|
|
111972
113148
|
let vox = v.mm2vox(mm);
|
|
111973
113149
|
let val = v.getValue(...vox);
|
|
111974
|
-
return val;
|
|
113150
|
+
return { name: v.name, value: val, id: v.id, mm, vox };
|
|
111975
113151
|
})
|
|
111976
113152
|
});
|
|
111977
113153
|
return;
|
|
111978
|
-
} else {
|
|
111979
|
-
if (x2 === null && y === null) {
|
|
111980
|
-
this.scene.crosshairPos[2 - axCorSag] = posChange;
|
|
111981
|
-
this.drawScene();
|
|
111982
|
-
return;
|
|
111983
|
-
}
|
|
111984
113154
|
}
|
|
111985
113155
|
}
|
|
111986
113156
|
};
|
|
111987
|
-
Niivue.prototype.
|
|
113157
|
+
Niivue.prototype.drawRuler = function() {
|
|
113158
|
+
let fovMM = [];
|
|
113159
|
+
let ltwh = [];
|
|
113160
|
+
for (let i2 = 0; i2 < this.screenSlices.length; i2++) {
|
|
113161
|
+
if (this.screenSlices[i2].axCorSag === this.sliceTypeRender)
|
|
113162
|
+
continue;
|
|
113163
|
+
if (this.screenSlices[i2].fovMM.length > 1) {
|
|
113164
|
+
ltwh = this.screenSlices[i2].leftTopWidthHeight;
|
|
113165
|
+
fovMM = this.screenSlices[i2].fovMM;
|
|
113166
|
+
break;
|
|
113167
|
+
}
|
|
113168
|
+
}
|
|
113169
|
+
if (ltwh.length < 4)
|
|
113170
|
+
return;
|
|
113171
|
+
let frac10cm = 100 / fovMM[0];
|
|
113172
|
+
let pix10cm = frac10cm * ltwh[2];
|
|
113173
|
+
let pixLeft = ltwh[0] + 0.5 * ltwh[2] - 0.5 * pix10cm;
|
|
113174
|
+
let pixTop = ltwh[1] + ltwh[3] - 2 * this.opts.rulerWidth;
|
|
113175
|
+
let startXYendXY = [pixLeft, pixTop, pixLeft + pix10cm, pixTop];
|
|
113176
|
+
this.drawRuler10cm(startXYendXY);
|
|
113177
|
+
};
|
|
113178
|
+
Niivue.prototype.drawRuler10cm = function(startXYendXY) {
|
|
113179
|
+
this.gl.bindVertexArray(this.genericVAO);
|
|
111988
113180
|
this.lineShader.use(this.gl);
|
|
111989
|
-
this.gl.uniform4fv(this.lineShader.uniforms["lineColor"], this.opts.
|
|
113181
|
+
this.gl.uniform4fv(this.lineShader.uniforms["lineColor"], this.opts.rulerColor);
|
|
111990
113182
|
this.gl.uniform2fv(this.lineShader.uniforms["canvasWidthHeight"], [
|
|
111991
113183
|
this.gl.canvas.width,
|
|
111992
113184
|
this.gl.canvas.height
|
|
111993
113185
|
]);
|
|
111994
|
-
this.gl.
|
|
113186
|
+
this.gl.uniform1f(this.lineShader.uniforms["thickness"], this.opts.rulerWidth);
|
|
113187
|
+
this.gl.uniform4fv(this.lineShader.uniforms["startXYendXY"], startXYendXY);
|
|
113188
|
+
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
113189
|
+
let w1cm = -0.1 * (startXYendXY[0] - startXYendXY[2]);
|
|
113190
|
+
let b = startXYendXY[1];
|
|
113191
|
+
let t = b - 2 * this.opts.rulerWidth;
|
|
113192
|
+
let t2 = b - 4 * this.opts.rulerWidth;
|
|
113193
|
+
for (let i2 = 0; i2 < 11; i2++) {
|
|
113194
|
+
let l = startXYendXY[0] + i2 * w1cm;
|
|
113195
|
+
let xyxy = [l, b, l, t];
|
|
113196
|
+
if (i2 % 5 === 0)
|
|
113197
|
+
xyxy[3] = t2;
|
|
113198
|
+
this.gl.uniform4fv(this.lineShader.uniforms["startXYendXY"], xyxy);
|
|
113199
|
+
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
113200
|
+
}
|
|
113201
|
+
this.gl.bindVertexArray(this.unusedVAO);
|
|
113202
|
+
};
|
|
113203
|
+
Niivue.prototype.drawMeasurementTool = function(startXYendXY) {
|
|
113204
|
+
let gl = this.gl;
|
|
113205
|
+
gl.bindVertexArray(this.genericVAO);
|
|
113206
|
+
gl.depthFunc(gl.ALWAYS);
|
|
113207
|
+
gl.enable(gl.BLEND);
|
|
113208
|
+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
113209
|
+
this.lineShader.use(this.gl);
|
|
113210
|
+
gl.uniform4fv(this.lineShader.uniforms["lineColor"], this.opts.rulerColor);
|
|
113211
|
+
gl.uniform2fv(this.lineShader.uniforms["canvasWidthHeight"], [
|
|
113212
|
+
gl.canvas.width,
|
|
113213
|
+
gl.canvas.height
|
|
113214
|
+
]);
|
|
113215
|
+
gl.uniform1f(this.lineShader.uniforms["thickness"], this.opts.rulerWidth);
|
|
113216
|
+
gl.uniform4fv(this.lineShader.uniforms["startXYendXY"], startXYendXY);
|
|
113217
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
113218
|
+
let c2 = this.opts.rulerColor;
|
|
113219
|
+
c2[3] = 1;
|
|
113220
|
+
gl.uniform4fv(this.lineShader.uniforms["lineColor"], c2);
|
|
113221
|
+
let w = this.opts.rulerWidth;
|
|
113222
|
+
gl.uniform1f(this.lineShader.uniforms["thickness"], w * 2);
|
|
113223
|
+
let sXYeXY = [
|
|
113224
|
+
startXYendXY[0],
|
|
113225
|
+
startXYendXY[1] - w,
|
|
113226
|
+
startXYendXY[0],
|
|
113227
|
+
startXYendXY[1] + w
|
|
113228
|
+
];
|
|
113229
|
+
gl.uniform4fv(this.lineShader.uniforms["startXYendXY"], sXYeXY);
|
|
113230
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
113231
|
+
sXYeXY = [
|
|
113232
|
+
startXYendXY[2],
|
|
113233
|
+
startXYendXY[3] - w,
|
|
113234
|
+
startXYendXY[2],
|
|
113235
|
+
startXYendXY[3] + w
|
|
113236
|
+
];
|
|
113237
|
+
gl.uniform4fv(this.lineShader.uniforms["startXYendXY"], sXYeXY);
|
|
113238
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
113239
|
+
let startXY = this.canvasPos2frac([startXYendXY[0], startXYendXY[1]]);
|
|
113240
|
+
let endXY = this.canvasPos2frac([startXYendXY[2], startXYendXY[3]]);
|
|
113241
|
+
if (startXY[0] >= 0 && endXY[0] >= 0) {
|
|
113242
|
+
startXY = this.frac2mm(startXY);
|
|
113243
|
+
endXY = this.frac2mm(endXY);
|
|
113244
|
+
let v = create$1();
|
|
113245
|
+
sub(v, startXY, endXY);
|
|
113246
|
+
let lenMM = len(v);
|
|
113247
|
+
let decimals = 2;
|
|
113248
|
+
if (lenMM > 9)
|
|
113249
|
+
decimals = 1;
|
|
113250
|
+
if (lenMM > 99)
|
|
113251
|
+
decimals = 0;
|
|
113252
|
+
let stringMM = lenMM.toFixed(decimals);
|
|
113253
|
+
this.drawTextBetween(startXYendXY, stringMM);
|
|
113254
|
+
}
|
|
113255
|
+
gl.bindVertexArray(this.unusedVAO);
|
|
113256
|
+
};
|
|
113257
|
+
Niivue.prototype.drawBox = function(leftTopWidthHeight, lineColor = [1, 0, 0, 1]) {
|
|
113258
|
+
this.rectShader.use(this.gl);
|
|
113259
|
+
this.gl.enable(this.gl.BLEND);
|
|
113260
|
+
this.gl.uniform4fv(this.rectShader.uniforms["lineColor"], lineColor);
|
|
113261
|
+
this.gl.uniform2fv(this.rectShader.uniforms["canvasWidthHeight"], [
|
|
113262
|
+
this.gl.canvas.width,
|
|
113263
|
+
this.gl.canvas.height
|
|
113264
|
+
]);
|
|
113265
|
+
this.gl.uniform4f(this.rectShader.uniforms["leftTopWidthHeight"], leftTopWidthHeight[0], leftTopWidthHeight[1], leftTopWidthHeight[2], leftTopWidthHeight[3]);
|
|
111995
113266
|
this.gl.bindVertexArray(this.genericVAO);
|
|
111996
113267
|
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
111997
113268
|
this.gl.bindVertexArray(this.unusedVAO);
|
|
111998
113269
|
};
|
|
111999
|
-
Niivue.prototype.
|
|
113270
|
+
Niivue.prototype.drawSelectionBox = function(leftTopWidthHeight, lineColor = [1, 0, 0, 1]) {
|
|
113271
|
+
this.drawBox(leftTopWidthHeight, this.opts.selectionBoxColor);
|
|
113272
|
+
};
|
|
113273
|
+
function tickSpacingX(tickCount, mn, mx) {
|
|
113274
|
+
let range = Math.abs(mx - mn);
|
|
113275
|
+
if (range === 0)
|
|
113276
|
+
return [0, 0];
|
|
113277
|
+
let unroundedTickSize = range / (tickCount - 1);
|
|
113278
|
+
let x2 = Math.ceil(Math.log10(unroundedTickSize) - 1);
|
|
113279
|
+
let pow10x = Math.pow(10, x2);
|
|
113280
|
+
let spacing = Math.ceil(unroundedTickSize / pow10x) * pow10x;
|
|
113281
|
+
let ticMin = mn;
|
|
113282
|
+
if (ticMin % spacing !== 0 && range % spacing !== 0)
|
|
113283
|
+
ticMin = Math.floor((mn + spacing) / spacing) * spacing;
|
|
113284
|
+
if (mn / spacing % 1 !== 0)
|
|
113285
|
+
ticMin = Math.sign(ticMin) * Math.round(Math.abs(ticMin));
|
|
113286
|
+
return [spacing, ticMin];
|
|
113287
|
+
}
|
|
113288
|
+
function tickSpacing(mn, mx) {
|
|
113289
|
+
let range = Math.abs(mx - mn);
|
|
113290
|
+
let [spacing, ticMin] = tickSpacingX(5, mn, mx);
|
|
113291
|
+
if (range % spacing === 0)
|
|
113292
|
+
return [spacing, ticMin];
|
|
113293
|
+
[spacing, ticMin] = tickSpacingX(4, mn, mx);
|
|
113294
|
+
if (range % spacing === 0)
|
|
113295
|
+
return [spacing, ticMin];
|
|
113296
|
+
[spacing, ticMin] = tickSpacingX(5, mn, mx);
|
|
113297
|
+
return [spacing, ticMin];
|
|
113298
|
+
}
|
|
113299
|
+
Niivue.prototype.effectiveCanvasHeight = function() {
|
|
113300
|
+
return this.gl.canvas.height - this.colorbarHeight;
|
|
113301
|
+
};
|
|
113302
|
+
Niivue.prototype.reserveColorbarPanel = function() {
|
|
113303
|
+
let txtHt = Math.max(this.opts.textHeight, 0.01);
|
|
113304
|
+
txtHt = txtHt * Math.min(this.gl.canvas.height, this.gl.canvas.width);
|
|
113305
|
+
let fullHt = 3 * txtHt;
|
|
113306
|
+
let leftTopWidthHeight = [
|
|
113307
|
+
0,
|
|
113308
|
+
this.gl.canvas.height - fullHt,
|
|
113309
|
+
this.gl.canvas.width,
|
|
113310
|
+
fullHt
|
|
113311
|
+
];
|
|
113312
|
+
this.colorbarHeight = leftTopWidthHeight[3] + 1;
|
|
113313
|
+
return leftTopWidthHeight;
|
|
113314
|
+
};
|
|
113315
|
+
Niivue.prototype.drawColorbarCore = function(layer = 0, leftTopWidthHeight = [0, 0, 0, 0], isNegativeColor = false) {
|
|
113316
|
+
if (this.volumes.length < 1)
|
|
113317
|
+
return;
|
|
113318
|
+
if (layer >= this.volumes.length)
|
|
113319
|
+
layer = 0;
|
|
112000
113320
|
if (leftTopWidthHeight[2] <= 0 || leftTopWidthHeight[3] <= 0)
|
|
112001
113321
|
return;
|
|
113322
|
+
let txtHt = Math.max(this.opts.textHeight, 0.01);
|
|
113323
|
+
txtHt = txtHt * Math.min(this.gl.canvas.height, this.gl.canvas.width);
|
|
113324
|
+
let margin = txtHt;
|
|
113325
|
+
let fullHt = 3 * txtHt;
|
|
113326
|
+
let barHt = txtHt;
|
|
113327
|
+
if (leftTopWidthHeight[3] < fullHt) {
|
|
113328
|
+
if (leftTopWidthHeight[3] < 3)
|
|
113329
|
+
return;
|
|
113330
|
+
margin = 1;
|
|
113331
|
+
barHt = leftTopWidthHeight[3] - 2;
|
|
113332
|
+
}
|
|
113333
|
+
this.gl.disable(this.gl.DEPTH_TEST);
|
|
113334
|
+
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
|
+
let barLTWH = [
|
|
113343
|
+
leftTopWidthHeight[0] + margin,
|
|
113344
|
+
leftTopWidthHeight[1],
|
|
113345
|
+
leftTopWidthHeight[2] - 2 * margin,
|
|
113346
|
+
barHt
|
|
113347
|
+
];
|
|
113348
|
+
let rimLTWH = [
|
|
113349
|
+
barLTWH[0] - 1,
|
|
113350
|
+
barLTWH[1] - 1,
|
|
113351
|
+
barLTWH[2] + 2,
|
|
113352
|
+
barLTWH[3] + 2
|
|
113353
|
+
];
|
|
113354
|
+
this.drawBox(rimLTWH, this.opts.crosshairColor);
|
|
112002
113355
|
this.colorbarShader.use(this.gl);
|
|
112003
113356
|
this.gl.activeTexture(this.gl.TEXTURE1);
|
|
112004
113357
|
this.gl.bindTexture(this.gl.TEXTURE_2D, this.colormapTexture);
|
|
112005
113358
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST);
|
|
112006
113359
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST);
|
|
113360
|
+
let lx = layer;
|
|
113361
|
+
if (isNegativeColor)
|
|
113362
|
+
lx += this.volumes.length;
|
|
113363
|
+
this.gl.uniform1f(this.colorbarShader.uniforms["layer"], lx);
|
|
112007
113364
|
this.gl.uniform2fv(this.colorbarShader.uniforms["canvasWidthHeight"], [
|
|
112008
113365
|
this.gl.canvas.width,
|
|
112009
113366
|
this.gl.canvas.height
|
|
112010
113367
|
]);
|
|
112011
|
-
this.gl.
|
|
113368
|
+
this.gl.uniform4fv(this.colorbarShader.uniforms["leftTopWidthHeight"], barLTWH);
|
|
112012
113369
|
this.gl.bindVertexArray(this.genericVAO);
|
|
112013
113370
|
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
112014
113371
|
this.gl.bindVertexArray(this.unusedVAO);
|
|
112015
113372
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
|
|
112016
113373
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
|
|
113374
|
+
let min2 = this.volumes[layer].cal_min;
|
|
113375
|
+
let max2 = this.volumes[layer].cal_max;
|
|
113376
|
+
if (min2 >= max2 || txtHt < 1)
|
|
113377
|
+
return;
|
|
113378
|
+
let range = max2 - min2;
|
|
113379
|
+
let [spacing, ticMin] = tickSpacing(min2, max2);
|
|
113380
|
+
function humanize(x2) {
|
|
113381
|
+
return x2.toFixed(6).replace(/\.?0*$/, "");
|
|
113382
|
+
}
|
|
113383
|
+
let tic = ticMin;
|
|
113384
|
+
let ticLTWH = [0, barLTWH[1] + barLTWH[3] - txtHt * 0.5, 2, txtHt * 0.75];
|
|
113385
|
+
let txtTop = ticLTWH[1] + ticLTWH[3];
|
|
113386
|
+
while (tic <= max2) {
|
|
113387
|
+
ticLTWH[0] = barLTWH[0] + (tic - min2) / range * barLTWH[2];
|
|
113388
|
+
this.drawBox(ticLTWH);
|
|
113389
|
+
let str = humanize(tic);
|
|
113390
|
+
if (isNegativeColor)
|
|
113391
|
+
str = "-" + str;
|
|
113392
|
+
this.drawTextBelow([ticLTWH[0], txtTop], str);
|
|
113393
|
+
tic += spacing;
|
|
113394
|
+
}
|
|
113395
|
+
};
|
|
113396
|
+
Niivue.prototype.drawColorbar = function() {
|
|
113397
|
+
let nlayers = this.volumes.length;
|
|
113398
|
+
if (nlayers < 1)
|
|
113399
|
+
return;
|
|
113400
|
+
let ncolorMapNegative = 0;
|
|
113401
|
+
for (let i2 = 0; i2 < nlayers; i2++)
|
|
113402
|
+
if (this.volumes[i2].colorMapNegative.length > 0)
|
|
113403
|
+
ncolorMapNegative++;
|
|
113404
|
+
let leftTopWidthHeight = this.reserveColorbarPanel();
|
|
113405
|
+
let txtHt = Math.max(this.opts.textHeight, 0.01);
|
|
113406
|
+
txtHt = txtHt * Math.min(this.gl.canvas.height, this.gl.canvas.width);
|
|
113407
|
+
let fullHt = 3 * txtHt;
|
|
113408
|
+
let wid = leftTopWidthHeight[2] / (nlayers + ncolorMapNegative);
|
|
113409
|
+
if (leftTopWidthHeight[2] <= 0 || leftTopWidthHeight[3] <= 0) {
|
|
113410
|
+
wid = this.gl.canvas.width / nlayers;
|
|
113411
|
+
leftTopWidthHeight = [0, this.gl.canvas.height - fullHt, wid, fullHt];
|
|
113412
|
+
}
|
|
113413
|
+
leftTopWidthHeight[2] = wid;
|
|
113414
|
+
for (let i2 = 0; i2 < nlayers; i2++) {
|
|
113415
|
+
this.drawColorbarCore(i2, leftTopWidthHeight);
|
|
113416
|
+
leftTopWidthHeight[0] += wid;
|
|
113417
|
+
if (this.volumes[i2].colorMapNegative.length === 0)
|
|
113418
|
+
continue;
|
|
113419
|
+
this.drawColorbarCore(i2, leftTopWidthHeight, true);
|
|
113420
|
+
leftTopWidthHeight[0] += wid;
|
|
113421
|
+
}
|
|
112017
113422
|
};
|
|
112018
113423
|
Niivue.prototype.textWidth = function(scale2, str) {
|
|
112019
113424
|
let w = 0;
|
|
@@ -112074,6 +113479,34 @@ Niivue.prototype.drawTextLeft = function(xy, str, scale2 = 1, color = null) {
|
|
|
112074
113479
|
xy[1] -= 0.5 * size;
|
|
112075
113480
|
this.drawText(xy, str, scale2, color);
|
|
112076
113481
|
};
|
|
113482
|
+
Niivue.prototype.drawTextRightBelow = function(xy, str, scale2 = 1, color = null) {
|
|
113483
|
+
if (this.opts.textHeight <= 0)
|
|
113484
|
+
return;
|
|
113485
|
+
this.opts.textHeight * this.gl.canvas.height * scale2;
|
|
113486
|
+
this.drawText(xy, str, scale2, color);
|
|
113487
|
+
};
|
|
113488
|
+
Niivue.prototype.drawTextBetween = function(startXYendXY, str, scale2 = 1, color = null) {
|
|
113489
|
+
if (this.opts.textHeight <= 0)
|
|
113490
|
+
return;
|
|
113491
|
+
let xy = [
|
|
113492
|
+
(startXYendXY[0] + startXYendXY[2]) * 0.5,
|
|
113493
|
+
(startXYendXY[1] + startXYendXY[3]) * 0.5
|
|
113494
|
+
];
|
|
113495
|
+
let size = this.opts.textHeight * this.gl.canvas.height * scale2;
|
|
113496
|
+
let w = this.textWidth(size, str);
|
|
113497
|
+
xy[0] -= 0.5 * w;
|
|
113498
|
+
xy[1] -= 0.5 * size;
|
|
113499
|
+
let LTWH = [xy[0] - 1, xy[1] - 1, w + 2, size + 2];
|
|
113500
|
+
let clr = color;
|
|
113501
|
+
if (clr === null)
|
|
113502
|
+
clr = this.opts.crosshairColor;
|
|
113503
|
+
if (clr[0] + clr[1] + clr[2] > 0.8)
|
|
113504
|
+
clr = [0, 0, 0, 0.5];
|
|
113505
|
+
else
|
|
113506
|
+
clr = [1, 1, 1, 0.5];
|
|
113507
|
+
this.drawRect(LTWH, clr);
|
|
113508
|
+
this.drawText(xy, str, scale2, color);
|
|
113509
|
+
};
|
|
112077
113510
|
Niivue.prototype.drawTextBelow = function(xy, str, scale2 = 1, color = null) {
|
|
112078
113511
|
if (this.opts.textHeight <= 0)
|
|
112079
113512
|
return;
|
|
@@ -112107,114 +113540,271 @@ Niivue.prototype.setInterpolation = function(isNearest) {
|
|
|
112107
113540
|
this.updateInterpolation(1);
|
|
112108
113541
|
this.drawScene();
|
|
112109
113542
|
};
|
|
112110
|
-
|
|
112111
|
-
|
|
112112
|
-
|
|
112113
|
-
|
|
113543
|
+
function deg2rad(deg) {
|
|
113544
|
+
return deg * (Math.PI / 180);
|
|
113545
|
+
}
|
|
113546
|
+
Niivue.prototype.calculateMvpMatrix2D = function(leftTopWidthHeight, mn, mx, clipTolerance = Infinity, clipDepth = 0, azimuth = null, elevation = null, isRadiolgical) {
|
|
113547
|
+
let gl = this.gl;
|
|
113548
|
+
gl.viewport(leftTopWidthHeight[0], this.gl.canvas.clientHeight - (leftTopWidthHeight[1] + leftTopWidthHeight[3]), leftTopWidthHeight[2], leftTopWidthHeight[3]);
|
|
113549
|
+
let left = mn[0];
|
|
113550
|
+
let right = mx[0];
|
|
113551
|
+
let leftTopMM = [left, mn[1]];
|
|
113552
|
+
let fovMM = [right - left, mx[1] - mn[1]];
|
|
113553
|
+
if (isRadiolgical) {
|
|
113554
|
+
leftTopMM = [mx[0], mn[1]];
|
|
113555
|
+
fovMM = [mn[0] - mx[0], mx[1] - mn[1]];
|
|
113556
|
+
left = -mx[0];
|
|
113557
|
+
right = -mn[0];
|
|
113558
|
+
}
|
|
113559
|
+
let scale2 = 2 * Math.max(Math.abs(mn[2]), Math.abs(mx[2]));
|
|
112114
113560
|
let projectionMatrix = create$2();
|
|
112115
|
-
|
|
112116
|
-
|
|
112117
|
-
|
|
112118
|
-
|
|
113561
|
+
let near = 0.01;
|
|
113562
|
+
let far = scale2 * 8;
|
|
113563
|
+
if (clipTolerance !== Infinity) {
|
|
113564
|
+
let r = isRadiolgical;
|
|
113565
|
+
if (elevation === 0 && (azimuth === 0 || azimuth === 180))
|
|
113566
|
+
r = !r;
|
|
113567
|
+
let dx = scale2 * 1.8 - clipDepth;
|
|
113568
|
+
if (!r)
|
|
113569
|
+
dx = scale2 * 1.8 + clipDepth;
|
|
113570
|
+
near = dx - clipTolerance;
|
|
113571
|
+
far = dx + clipTolerance;
|
|
113572
|
+
}
|
|
113573
|
+
ortho(projectionMatrix, left, right, mn[1], mx[1], near, far);
|
|
112119
113574
|
const modelMatrix = create$2();
|
|
112120
113575
|
modelMatrix[0] = -1;
|
|
112121
113576
|
let translateVec3 = fromValues$1(0, 0, -scale2 * 1.8);
|
|
112122
113577
|
translate(modelMatrix, modelMatrix, translateVec3);
|
|
112123
|
-
|
|
112124
|
-
|
|
112125
|
-
translate(modelMatrix, modelMatrix, [
|
|
112126
|
-
-origin[0],
|
|
112127
|
-
-origin[1],
|
|
112128
|
-
-origin[2]
|
|
112129
|
-
]);
|
|
113578
|
+
rotateX(modelMatrix, modelMatrix, deg2rad(270 - elevation));
|
|
113579
|
+
rotateZ(modelMatrix, modelMatrix, deg2rad(azimuth - 180));
|
|
112130
113580
|
let iModelMatrix = create$2();
|
|
112131
113581
|
invert(iModelMatrix, modelMatrix);
|
|
112132
113582
|
let normalMatrix = create$2();
|
|
112133
113583
|
transpose(normalMatrix, iModelMatrix);
|
|
112134
113584
|
let modelViewProjectionMatrix = create$2();
|
|
112135
|
-
multiply
|
|
112136
|
-
return
|
|
113585
|
+
multiply(modelViewProjectionMatrix, projectionMatrix, modelMatrix);
|
|
113586
|
+
return {
|
|
113587
|
+
modelViewProjectionMatrix,
|
|
113588
|
+
modelMatrix,
|
|
113589
|
+
normalMatrix,
|
|
113590
|
+
leftTopMM,
|
|
113591
|
+
fovMM
|
|
113592
|
+
};
|
|
112137
113593
|
};
|
|
112138
|
-
Niivue.prototype.
|
|
112139
|
-
if (this.
|
|
113594
|
+
Niivue.prototype.swizzleVec3MM = function(v3, axCorSag) {
|
|
113595
|
+
if (axCorSag === this.sliceTypeCoronal) {
|
|
113596
|
+
v3 = swizzleVec3(v3, [0, 2, 1]);
|
|
113597
|
+
} else if (axCorSag === this.sliceTypeSagittal) {
|
|
113598
|
+
v3 = swizzleVec3(v3, [1, 2, 0]);
|
|
113599
|
+
}
|
|
113600
|
+
return v3;
|
|
113601
|
+
};
|
|
113602
|
+
Niivue.prototype.screenFieldOfViewMM = function(axCorSag = 0, forceSliceMM = false) {
|
|
113603
|
+
if (!forceSliceMM && !this.opts.isSliceMM) {
|
|
113604
|
+
let fov = this.volumeObject3D.fieldOfViewDeObliqueMM.slice();
|
|
113605
|
+
return this.swizzleVec3MM(fov, axCorSag);
|
|
113606
|
+
}
|
|
113607
|
+
let mnMM = this.volumeObject3D.extentsMin.slice();
|
|
113608
|
+
let mxMM = this.volumeObject3D.extentsMax.slice();
|
|
113609
|
+
create$2();
|
|
113610
|
+
mnMM = this.swizzleVec3MM(mnMM, axCorSag);
|
|
113611
|
+
mxMM = this.swizzleVec3MM(mxMM, axCorSag);
|
|
113612
|
+
let fovMM = create$1();
|
|
113613
|
+
subtract(fovMM, mxMM, mnMM);
|
|
113614
|
+
return fovMM;
|
|
113615
|
+
};
|
|
113616
|
+
Niivue.prototype.screenFieldOfViewExtendedMM = function(axCorSag = 0) {
|
|
113617
|
+
let mnMM = this.volumeObject3D.extentsMin.slice();
|
|
113618
|
+
let mxMM = this.volumeObject3D.extentsMax.slice();
|
|
113619
|
+
let rotation = create$2();
|
|
113620
|
+
mnMM = this.swizzleVec3MM(mnMM, axCorSag);
|
|
113621
|
+
mxMM = this.swizzleVec3MM(mxMM, axCorSag);
|
|
113622
|
+
let fovMM = create$1();
|
|
113623
|
+
subtract(fovMM, mxMM, mnMM);
|
|
113624
|
+
return { mnMM, mxMM, rotation, fovMM };
|
|
113625
|
+
};
|
|
113626
|
+
Niivue.prototype.drawSliceOrientationText = function(leftTopWidthHeight, axCorSag) {
|
|
113627
|
+
this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
|
|
113628
|
+
let topText = "S";
|
|
113629
|
+
if (axCorSag === this.sliceTypeAxial)
|
|
113630
|
+
topText = "A";
|
|
113631
|
+
let leftText = this.opts.isRadiologicalConvention ? "R" : "L";
|
|
113632
|
+
if (axCorSag === this.sliceTypeSagittal)
|
|
113633
|
+
leftText = this.opts.sagittalNoseLeft ? "A" : "P";
|
|
113634
|
+
if (this.opts.isCornerOrientationText) {
|
|
113635
|
+
this.drawTextRightBelow([leftTopWidthHeight[0], leftTopWidthHeight[1]], leftText + topText);
|
|
112140
113636
|
return;
|
|
113637
|
+
}
|
|
113638
|
+
this.drawTextBelow([
|
|
113639
|
+
leftTopWidthHeight[0] + leftTopWidthHeight[2] * 0.5,
|
|
113640
|
+
leftTopWidthHeight[1]
|
|
113641
|
+
], topText);
|
|
113642
|
+
this.drawTextRight([
|
|
113643
|
+
leftTopWidthHeight[0],
|
|
113644
|
+
leftTopWidthHeight[1] + leftTopWidthHeight[3] * 0.5
|
|
113645
|
+
], leftText);
|
|
113646
|
+
};
|
|
113647
|
+
Niivue.prototype.xyMM2xyzMM = function(axCorSag, sliceFrac) {
|
|
113648
|
+
let sliceDim = 2;
|
|
113649
|
+
if (axCorSag === this.sliceTypeCoronal)
|
|
113650
|
+
sliceDim = 1;
|
|
113651
|
+
if (axCorSag === this.sliceTypeSagittal)
|
|
113652
|
+
sliceDim = 0;
|
|
113653
|
+
let a = [0, 0, 0];
|
|
113654
|
+
let b = [1, 1, 0];
|
|
113655
|
+
let c2 = [1, 0, 1];
|
|
113656
|
+
a[sliceDim] = sliceFrac;
|
|
113657
|
+
b[sliceDim] = sliceFrac;
|
|
113658
|
+
c2[sliceDim] = sliceFrac;
|
|
113659
|
+
a = this.frac2mm(a);
|
|
113660
|
+
b = this.frac2mm(b);
|
|
113661
|
+
c2 = this.frac2mm(c2);
|
|
113662
|
+
a = this.swizzleVec3MM(a, axCorSag);
|
|
113663
|
+
b = this.swizzleVec3MM(b, axCorSag);
|
|
113664
|
+
c2 = this.swizzleVec3MM(c2, axCorSag);
|
|
113665
|
+
let denom = (b[0] - a[0]) * (c2[1] - a[1]) - (c2[0] - a[0]) * (b[1] - a[1]);
|
|
113666
|
+
let yMult = (b[0] - a[0]) * (c2[2] - a[2]) - (c2[0] - a[0]) * (b[2] - a[2]);
|
|
113667
|
+
yMult /= denom;
|
|
113668
|
+
let xMult = (b[1] - a[1]) * (c2[2] - a[2]) - (c2[1] - a[1]) * (b[2] - a[2]);
|
|
113669
|
+
xMult /= denom;
|
|
113670
|
+
let AxyzMxy = [0, 0, 0, 0, 0];
|
|
113671
|
+
AxyzMxy[0] = a[0];
|
|
113672
|
+
AxyzMxy[1] = a[1];
|
|
113673
|
+
AxyzMxy[2] = a[2];
|
|
113674
|
+
AxyzMxy[3] = xMult;
|
|
113675
|
+
AxyzMxy[4] = yMult;
|
|
113676
|
+
return AxyzMxy;
|
|
113677
|
+
};
|
|
113678
|
+
Niivue.prototype.draw2DMM = function(leftTopWidthHeight, axCorSag, customMM = NaN) {
|
|
113679
|
+
let screen2 = this.screenFieldOfViewExtendedMM(axCorSag);
|
|
113680
|
+
let isRadiolgical = this.opts.isRadiologicalConvention && axCorSag < this.sliceTypeSagittal;
|
|
113681
|
+
if (customMM === Infinity || customMM === -Infinity) {
|
|
113682
|
+
isRadiolgical = customMM !== Infinity;
|
|
113683
|
+
if (axCorSag === this.sliceTypeCoronal)
|
|
113684
|
+
isRadiolgical = !isRadiolgical;
|
|
113685
|
+
} else if (this.opts.sagittalNoseLeft && axCorSag === this.sliceTypeSagittal)
|
|
113686
|
+
isRadiolgical = !isRadiolgical;
|
|
113687
|
+
let elevation = 0;
|
|
113688
|
+
let azimuth = 0;
|
|
113689
|
+
if (axCorSag === this.sliceTypeSagittal) {
|
|
113690
|
+
azimuth = isRadiolgical ? 90 : -90;
|
|
113691
|
+
} else if (axCorSag === this.sliceTypeCoronal) {
|
|
113692
|
+
azimuth = isRadiolgical ? 180 : 0;
|
|
113693
|
+
} else {
|
|
113694
|
+
azimuth = isRadiolgical ? 180 : 0;
|
|
113695
|
+
elevation = isRadiolgical ? -90 : 90;
|
|
113696
|
+
}
|
|
113697
|
+
if (leftTopWidthHeight[2] === 0 || leftTopWidthHeight[3] === 0)
|
|
113698
|
+
leftTopWidthHeight = this.scaleSlice(screen2.fovMM[0], screen2.fovMM[1]);
|
|
113699
|
+
if (isNaN(customMM)) {
|
|
113700
|
+
let panXY = this.swizzleVec3MM(this.scene.pan2Dxyzmm, axCorSag);
|
|
113701
|
+
screen2.mnMM[0] -= panXY[0];
|
|
113702
|
+
screen2.mxMM[0] -= panXY[0];
|
|
113703
|
+
screen2.mnMM[1] -= panXY[1];
|
|
113704
|
+
screen2.mxMM[1] -= panXY[1];
|
|
113705
|
+
screen2.mnMM[0] /= this.scene.pan2Dxyzmm[3];
|
|
113706
|
+
screen2.mxMM[0] /= this.scene.pan2Dxyzmm[3];
|
|
113707
|
+
screen2.mnMM[1] /= this.scene.pan2Dxyzmm[3];
|
|
113708
|
+
screen2.mxMM[1] /= this.scene.pan2Dxyzmm[3];
|
|
113709
|
+
}
|
|
113710
|
+
let sliceDim = 2;
|
|
113711
|
+
if (axCorSag === this.sliceTypeCoronal)
|
|
113712
|
+
sliceDim = 1;
|
|
113713
|
+
if (axCorSag === this.sliceTypeSagittal)
|
|
113714
|
+
sliceDim = 0;
|
|
113715
|
+
let sliceFrac = this.scene.crosshairPos[sliceDim];
|
|
113716
|
+
let mm = this.frac2mm(this.scene.crosshairPos);
|
|
113717
|
+
if (!isNaN(customMM) && customMM !== Infinity && customMM !== -Infinity) {
|
|
113718
|
+
mm = this.frac2mm([0.5, 0.5, 0.5]);
|
|
113719
|
+
mm[sliceDim] = customMM;
|
|
113720
|
+
let frac = this.mm2frac(mm);
|
|
113721
|
+
sliceFrac = frac[sliceDim];
|
|
113722
|
+
}
|
|
113723
|
+
let sliceMM = mm[sliceDim];
|
|
112141
113724
|
let gl = this.gl;
|
|
112142
|
-
|
|
112143
|
-
|
|
113725
|
+
gl.clear(gl.DEPTH_BUFFER_BIT);
|
|
113726
|
+
let obj = this.calculateMvpMatrix2D(leftTopWidthHeight, screen2.mnMM, screen2.mxMM, Infinity, 0, azimuth, elevation, isRadiolgical);
|
|
113727
|
+
if (customMM === Infinity || customMM === -Infinity) {
|
|
113728
|
+
this.draw3D(leftTopWidthHeight, obj.modelViewProjectionMatrix, obj.modelMatrix, obj.normalMatrix, azimuth, elevation);
|
|
113729
|
+
return;
|
|
113730
|
+
}
|
|
112144
113731
|
gl.enable(gl.DEPTH_TEST);
|
|
112145
113732
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
112146
113733
|
gl.disable(gl.BLEND);
|
|
112147
113734
|
gl.depthFunc(gl.GREATER);
|
|
112148
|
-
if (isDepthTest) {
|
|
112149
|
-
gl.disable(gl.BLEND);
|
|
112150
|
-
gl.depthFunc(gl.GREATER);
|
|
112151
|
-
} else {
|
|
112152
|
-
gl.enable(gl.BLEND);
|
|
112153
|
-
gl.depthFunc(gl.ALWAYS);
|
|
112154
|
-
}
|
|
112155
113735
|
gl.disable(gl.CULL_FACE);
|
|
112156
|
-
this.
|
|
112157
|
-
gl.
|
|
112158
|
-
gl.
|
|
112159
|
-
gl.
|
|
112160
|
-
gl.uniform1f(this.
|
|
112161
|
-
|
|
112162
|
-
|
|
112163
|
-
|
|
112164
|
-
|
|
112165
|
-
|
|
112166
|
-
|
|
112167
|
-
|
|
112168
|
-
|
|
112169
|
-
|
|
112170
|
-
|
|
112171
|
-
|
|
112172
|
-
|
|
112173
|
-
|
|
112174
|
-
|
|
112175
|
-
|
|
112176
|
-
return;
|
|
113736
|
+
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);
|
|
113746
|
+
this.screenSlices.push({
|
|
113747
|
+
leftTopWidthHeight,
|
|
113748
|
+
axCorSag,
|
|
113749
|
+
sliceFrac,
|
|
113750
|
+
AxyzMxy: this.xyMM2xyzMM(axCorSag, sliceFrac),
|
|
113751
|
+
leftTopMM: obj.leftTopMM,
|
|
113752
|
+
fovMM: obj.fovMM
|
|
113753
|
+
});
|
|
113754
|
+
if (isNaN(customMM)) {
|
|
113755
|
+
this.drawCrosshairs3D(true, 1, obj.modelViewProjectionMatrix);
|
|
112177
113756
|
}
|
|
112178
|
-
|
|
112179
|
-
|
|
112180
|
-
|
|
112181
|
-
|
|
112182
|
-
for (let i2 = 0; i2 < this.meshes.length; i2++) {
|
|
112183
|
-
if (this.meshes[i2].indexCount < 3)
|
|
112184
|
-
continue;
|
|
112185
|
-
if (!this.meshes[i2].offsetPt0)
|
|
112186
|
-
continue;
|
|
112187
|
-
gl.bindVertexArray(this.meshes[i2].vao);
|
|
112188
|
-
gl.drawElements(gl.LINE_STRIP, this.meshes[i2].indexCount, gl.UNSIGNED_INT, 0);
|
|
112189
|
-
gl.bindVertexArray(this.unusedVAO);
|
|
113757
|
+
if (this.opts.meshThicknessOn2D > 0) {
|
|
113758
|
+
if (this.opts.meshThicknessOn2D !== Infinity)
|
|
113759
|
+
obj = this.calculateMvpMatrix2D(leftTopWidthHeight, screen2.mnMM, screen2.mxMM, this.opts.meshThicknessOn2D, sliceMM, azimuth, elevation, isRadiolgical);
|
|
113760
|
+
this.drawMesh3D(true, 1, obj.modelViewProjectionMatrix, obj.modelMatrix, obj.normalMatrix);
|
|
112190
113761
|
}
|
|
112191
|
-
|
|
112192
|
-
|
|
113762
|
+
if (isNaN(customMM))
|
|
113763
|
+
this.drawCrosshairs3D(false, 0.15, obj.modelViewProjectionMatrix);
|
|
113764
|
+
this.drawSliceOrientationText(leftTopWidthHeight, axCorSag);
|
|
112193
113765
|
this.readyForSync = true;
|
|
112194
113766
|
};
|
|
112195
|
-
Niivue.prototype.
|
|
113767
|
+
Niivue.prototype.draw2DVox = function(leftTopWidthHeight, axCorSag, customMM = NaN) {
|
|
113768
|
+
let fovMM = this.screenFieldOfViewMM(axCorSag);
|
|
113769
|
+
if (leftTopWidthHeight[2] === 0 || leftTopWidthHeight[3] === 0) {
|
|
113770
|
+
leftTopWidthHeight = this.scaleSlice(fovMM[0], fovMM[1]);
|
|
113771
|
+
}
|
|
112196
113772
|
this.gl.cullFace(this.gl.FRONT);
|
|
113773
|
+
let ltwh = leftTopWidthHeight.slice();
|
|
112197
113774
|
let gl = this.gl;
|
|
112198
113775
|
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
|
112199
113776
|
gl.disable(gl.DEPTH_TEST);
|
|
112200
|
-
|
|
113777
|
+
let sliceDim = 2;
|
|
113778
|
+
let crossXYZ = [
|
|
112201
113779
|
this.scene.crosshairPos[0],
|
|
112202
113780
|
this.scene.crosshairPos[1],
|
|
112203
113781
|
this.scene.crosshairPos[2]
|
|
112204
113782
|
];
|
|
112205
|
-
if (axCorSag === this.sliceTypeCoronal)
|
|
113783
|
+
if (axCorSag === this.sliceTypeCoronal) {
|
|
113784
|
+
sliceDim = 1;
|
|
112206
113785
|
crossXYZ = [
|
|
112207
113786
|
this.scene.crosshairPos[0],
|
|
112208
113787
|
this.scene.crosshairPos[2],
|
|
112209
113788
|
this.scene.crosshairPos[1]
|
|
112210
113789
|
];
|
|
112211
|
-
|
|
113790
|
+
}
|
|
113791
|
+
if (axCorSag === this.sliceTypeSagittal) {
|
|
113792
|
+
sliceDim = 0;
|
|
112212
113793
|
crossXYZ = [
|
|
112213
113794
|
this.scene.crosshairPos[1],
|
|
112214
113795
|
this.scene.crosshairPos[2],
|
|
112215
113796
|
this.scene.crosshairPos[0]
|
|
112216
113797
|
];
|
|
113798
|
+
}
|
|
113799
|
+
if (!isNaN(customMM)) {
|
|
113800
|
+
let mm = this.frac2mm([0.5, 0.5, 0.5]);
|
|
113801
|
+
mm[sliceDim] = customMM;
|
|
113802
|
+
let frac = this.mm2frac(mm);
|
|
113803
|
+
crossXYZ[2] = frac[sliceDim];
|
|
113804
|
+
}
|
|
112217
113805
|
let isMirrorLR = this.opts.isRadiologicalConvention && axCorSag < this.sliceTypeSagittal;
|
|
113806
|
+
if (this.opts.sagittalNoseLeft && axCorSag === this.sliceTypeSagittal)
|
|
113807
|
+
isMirrorLR = !isMirrorLR;
|
|
112218
113808
|
this.sliceShader.use(this.gl);
|
|
112219
113809
|
this.gl.uniform1f(this.sliceShader.uniforms["opacity"], this.volumes[0].opacity);
|
|
112220
113810
|
this.gl.uniform1i(this.sliceShader.uniforms["backgroundMasksOverlays"], this.backgroundMasksOverlays);
|
|
@@ -112232,59 +113822,50 @@ Niivue.prototype.draw2D = function(leftTopWidthHeight, axCorSag) {
|
|
|
112232
113822
|
this.gl.uniform4f(this.sliceShader.uniforms["leftTopWidthHeight"], leftTopWidthHeight[0], leftTopWidthHeight[1], leftTopWidthHeight[2], leftTopWidthHeight[3]);
|
|
112233
113823
|
this.gl.bindVertexArray(this.genericVAO);
|
|
112234
113824
|
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
112235
|
-
this.screenSlices
|
|
112236
|
-
|
|
112237
|
-
|
|
113825
|
+
this.screenSlices.push({
|
|
113826
|
+
leftTopWidthHeight,
|
|
113827
|
+
axCorSag,
|
|
113828
|
+
sliceFrac: crossXYZ[2],
|
|
113829
|
+
AxyzMxy: [],
|
|
113830
|
+
leftTopMM: [],
|
|
113831
|
+
fovMM: [fovMM[0], fovMM[1]]
|
|
113832
|
+
});
|
|
113833
|
+
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
|
113834
|
+
this.drawSliceOrientationText(ltwh, axCorSag);
|
|
113835
|
+
if (!isNaN(customMM))
|
|
113836
|
+
return;
|
|
112238
113837
|
if (this.opts.crosshairWidth <= 0)
|
|
112239
113838
|
return;
|
|
112240
|
-
this.
|
|
112241
|
-
this.
|
|
112242
|
-
this.gl.
|
|
113839
|
+
this.gl.bindVertexArray(this.genericVAO);
|
|
113840
|
+
this.rectShader.use(this.gl);
|
|
113841
|
+
this.gl.uniform4fv(this.rectShader.uniforms["lineColor"], this.opts.crosshairColor);
|
|
113842
|
+
this.gl.uniform2fv(this.rectShader.uniforms["canvasWidthHeight"], [
|
|
112243
113843
|
this.gl.canvas.width,
|
|
112244
113844
|
this.gl.canvas.height
|
|
112245
113845
|
]);
|
|
112246
113846
|
var xleft = leftTopWidthHeight[0] + leftTopWidthHeight[2] * crossXYZ[0];
|
|
112247
|
-
this.gl.uniform4f(this.
|
|
113847
|
+
this.gl.uniform4f(this.rectShader.uniforms["leftTopWidthHeight"], xleft - 0.5 * this.opts.crosshairWidth, leftTopWidthHeight[1], this.opts.crosshairWidth, leftTopWidthHeight[3]);
|
|
112248
113848
|
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
112249
113849
|
var xtop = leftTopWidthHeight[1] + leftTopWidthHeight[3] * (1 - crossXYZ[1]);
|
|
112250
|
-
this.gl.uniform4f(this.
|
|
113850
|
+
this.gl.uniform4f(this.rectShader.uniforms["leftTopWidthHeight"], leftTopWidthHeight[0], xtop - 0.5 * this.opts.crosshairWidth, leftTopWidthHeight[2], this.opts.crosshairWidth);
|
|
112251
113851
|
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
112252
113852
|
this.gl.bindVertexArray(this.unusedVAO);
|
|
112253
113853
|
this.gl.enable(this.gl.CULL_FACE);
|
|
112254
|
-
if (isMirrorLR)
|
|
112255
|
-
this.drawTextRight([
|
|
112256
|
-
leftTopWidthHeight[0] + leftTopWidthHeight[2] + 1,
|
|
112257
|
-
leftTopWidthHeight[1] + 0.5 * leftTopWidthHeight[3]
|
|
112258
|
-
], "R");
|
|
112259
|
-
else if (axCorSag < this.sliceTypeSagittal)
|
|
112260
|
-
this.drawTextRight([
|
|
112261
|
-
leftTopWidthHeight[0] + 1,
|
|
112262
|
-
leftTopWidthHeight[1] + 0.5 * leftTopWidthHeight[3]
|
|
112263
|
-
], "L");
|
|
112264
|
-
if (axCorSag === this.sliceTypeAxial)
|
|
112265
|
-
this.drawTextBelow([
|
|
112266
|
-
leftTopWidthHeight[0] + 0.5 * leftTopWidthHeight[2],
|
|
112267
|
-
leftTopWidthHeight[1] + 1
|
|
112268
|
-
], "A");
|
|
112269
|
-
if (axCorSag > this.sliceTypeAxial)
|
|
112270
|
-
this.drawTextBelow([
|
|
112271
|
-
leftTopWidthHeight[0] + 0.5 * leftTopWidthHeight[2],
|
|
112272
|
-
leftTopWidthHeight[1] + 1
|
|
112273
|
-
], "S");
|
|
112274
|
-
if (axCorSag !== this.sliceTypeAxial)
|
|
112275
|
-
return;
|
|
112276
|
-
this.setPivot3D();
|
|
112277
|
-
gl.viewport(leftTopWidthHeight[0], leftTopWidthHeight[1], leftTopWidthHeight[2], leftTopWidthHeight[3]);
|
|
112278
113854
|
gl.enable(gl.DEPTH_TEST);
|
|
112279
113855
|
gl.depthFunc(gl.ALWAYS);
|
|
112280
113856
|
gl.clearDepth(0);
|
|
112281
113857
|
this.sync();
|
|
112282
113858
|
};
|
|
112283
|
-
Niivue.prototype.
|
|
112284
|
-
|
|
112285
|
-
|
|
112286
|
-
|
|
112287
|
-
|
|
113859
|
+
Niivue.prototype.draw2D = function(leftTopWidthHeight, axCorSag, customMM = NaN) {
|
|
113860
|
+
if (this.opts.isSliceMM)
|
|
113861
|
+
this.draw2DMM(leftTopWidthHeight, axCorSag, customMM);
|
|
113862
|
+
else
|
|
113863
|
+
this.draw2DVox(leftTopWidthHeight, axCorSag, customMM);
|
|
113864
|
+
};
|
|
113865
|
+
Niivue.prototype.calculateMvpMatrix = function(unused, leftTopWidthHeight = [0, 0, 0, 0], azimuth, elevation) {
|
|
113866
|
+
if (leftTopWidthHeight[2] === 0 || leftTopWidthHeight[3] === 0)
|
|
113867
|
+
leftTopWidthHeight = [0, 0, this.gl.canvas.width, this.gl.canvas.height];
|
|
113868
|
+
let whratio = leftTopWidthHeight[2] / leftTopWidthHeight[3];
|
|
112288
113869
|
let scale2 = this.furthestFromPivot;
|
|
112289
113870
|
let origin = this.pivot3D;
|
|
112290
113871
|
let projectionMatrix = create$2();
|
|
@@ -112299,8 +113880,8 @@ Niivue.prototype.calculateMvpMatrix = function() {
|
|
|
112299
113880
|
translate(modelMatrix, modelMatrix, translateVec3);
|
|
112300
113881
|
if (this.position)
|
|
112301
113882
|
translate(modelMatrix, modelMatrix, this.position);
|
|
112302
|
-
rotateX(modelMatrix, modelMatrix, deg2rad(270 -
|
|
112303
|
-
rotateZ(modelMatrix, modelMatrix, deg2rad(
|
|
113883
|
+
rotateX(modelMatrix, modelMatrix, deg2rad(270 - elevation));
|
|
113884
|
+
rotateZ(modelMatrix, modelMatrix, deg2rad(azimuth - 180));
|
|
112304
113885
|
translate(modelMatrix, modelMatrix, [
|
|
112305
113886
|
-origin[0],
|
|
112306
113887
|
-origin[1],
|
|
@@ -112311,24 +113892,24 @@ Niivue.prototype.calculateMvpMatrix = function() {
|
|
|
112311
113892
|
let normalMatrix = create$2();
|
|
112312
113893
|
transpose(normalMatrix, iModelMatrix);
|
|
112313
113894
|
let modelViewProjectionMatrix = create$2();
|
|
112314
|
-
multiply
|
|
113895
|
+
multiply(modelViewProjectionMatrix, projectionMatrix, modelMatrix);
|
|
112315
113896
|
return [modelViewProjectionMatrix, modelMatrix, normalMatrix];
|
|
112316
113897
|
};
|
|
112317
|
-
Niivue.prototype.calculateRayDirection = function() {
|
|
112318
|
-
function
|
|
113898
|
+
Niivue.prototype.calculateRayDirection = function(azimuth, elevation) {
|
|
113899
|
+
function deg2rad2(deg) {
|
|
112319
113900
|
return deg * (Math.PI / 180);
|
|
112320
113901
|
}
|
|
112321
113902
|
const modelMatrix = create$2();
|
|
112322
113903
|
modelMatrix[0] = -1;
|
|
112323
|
-
rotateX(modelMatrix, modelMatrix,
|
|
112324
|
-
rotateZ(modelMatrix, modelMatrix,
|
|
113904
|
+
rotateX(modelMatrix, modelMatrix, deg2rad2(270 - elevation));
|
|
113905
|
+
rotateZ(modelMatrix, modelMatrix, deg2rad2(azimuth - 180));
|
|
112325
113906
|
if (this.back.obliqueRAS) {
|
|
112326
113907
|
let oblique = clone$1(this.back.obliqueRAS);
|
|
112327
|
-
multiply
|
|
113908
|
+
multiply(modelMatrix, modelMatrix, oblique);
|
|
112328
113909
|
}
|
|
112329
113910
|
let projectionMatrix = fromValues$2(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1);
|
|
112330
113911
|
let mvpMatrix = create$2();
|
|
112331
|
-
multiply
|
|
113912
|
+
multiply(mvpMatrix, projectionMatrix, modelMatrix);
|
|
112332
113913
|
var inv = create$2();
|
|
112333
113914
|
invert(inv, mvpMatrix);
|
|
112334
113915
|
var rayDir4 = fromValues(0, 0, -1, 1);
|
|
@@ -112367,6 +113948,8 @@ Niivue.prototype.setPivot3D = function() {
|
|
|
112367
113948
|
scale(pivot, pivot, 0.5);
|
|
112368
113949
|
this.pivot3D = [pivot[0], pivot[1], pivot[2]];
|
|
112369
113950
|
subtract(pivot, mx, mn);
|
|
113951
|
+
this.extentsMin = mn;
|
|
113952
|
+
this.extentsMax = mx;
|
|
112370
113953
|
this.furthestFromPivot = length$1(pivot) * 0.5;
|
|
112371
113954
|
};
|
|
112372
113955
|
Niivue.prototype.drawGraphLine = function(LTRB, color = [1, 0, 0, 0.5], thickness = 2) {
|
|
@@ -112383,35 +113966,22 @@ Niivue.prototype.drawGraphLine = function(LTRB, color = [1, 0, 0, 0.5], thicknes
|
|
|
112383
113966
|
this.gl.bindVertexArray(this.unusedVAO);
|
|
112384
113967
|
};
|
|
112385
113968
|
Niivue.prototype.drawRect = function(LTWH, color = [1, 0, 0, 0.5]) {
|
|
112386
|
-
this.
|
|
112387
|
-
this.gl.uniform4fv(this.
|
|
112388
|
-
this.gl.uniform2fv(this.
|
|
113969
|
+
this.rectShader.use(this.gl);
|
|
113970
|
+
this.gl.uniform4fv(this.rectShader.uniforms["lineColor"], color);
|
|
113971
|
+
this.gl.uniform2fv(this.rectShader.uniforms["canvasWidthHeight"], [
|
|
112389
113972
|
this.gl.canvas.width,
|
|
112390
113973
|
this.gl.canvas.height
|
|
112391
113974
|
]);
|
|
112392
|
-
this.gl.uniform4f(this.
|
|
113975
|
+
this.gl.uniform4f(this.rectShader.uniforms["leftTopWidthHeight"], LTWH[0], LTWH[1], LTWH[2], LTWH[3]);
|
|
112393
113976
|
this.gl.bindVertexArray(this.genericVAO);
|
|
112394
113977
|
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
112395
113978
|
this.gl.bindVertexArray(this.unusedVAO);
|
|
112396
113979
|
};
|
|
112397
|
-
function tickSpacing(tickCount, mn, mx) {
|
|
112398
|
-
let range = Math.abs(mx - mn);
|
|
112399
|
-
if (range === 0)
|
|
112400
|
-
return [0, 0];
|
|
112401
|
-
let unroundedTickSize = range / (tickCount - 1);
|
|
112402
|
-
let x2 = Math.ceil(Math.log10(unroundedTickSize) - 1);
|
|
112403
|
-
let pow10x = Math.pow(10, x2);
|
|
112404
|
-
let spacing = Math.ceil(unroundedTickSize / pow10x) * pow10x;
|
|
112405
|
-
let ticMin = mn;
|
|
112406
|
-
if (mn / spacing % 1 !== 0)
|
|
112407
|
-
ticMin = Math.sign(ticMin) * Math.round(Math.abs(ticMin));
|
|
112408
|
-
return [spacing, ticMin];
|
|
112409
|
-
}
|
|
112410
113980
|
Niivue.prototype.drawGraph = function() {
|
|
112411
113981
|
this.gl;
|
|
112412
113982
|
let graph = this.graph;
|
|
112413
113983
|
if (this.graph.autoSizeMultiplanar && this.sliceType === this.sliceTypeMultiplanar) {
|
|
112414
|
-
for (let i2 = 0; i2 < this.
|
|
113984
|
+
for (let i2 = 0; i2 < this.screenSlices.length; i2++) {
|
|
112415
113985
|
var axCorSag = this.screenSlices[i2].axCorSag;
|
|
112416
113986
|
if (axCorSag !== this.sliceTypeSagittal)
|
|
112417
113987
|
continue;
|
|
@@ -112504,7 +114074,7 @@ Niivue.prototype.drawGraph = function() {
|
|
|
112504
114074
|
graph.backColor[3]
|
|
112505
114075
|
];
|
|
112506
114076
|
this.drawRect(graph.LTWH, borderColor);
|
|
112507
|
-
let [spacing, ticMin] = tickSpacing(
|
|
114077
|
+
let [spacing, ticMin] = tickSpacing(mn, mx);
|
|
112508
114078
|
function humanize(x2) {
|
|
112509
114079
|
return x2.toFixed(6).replace(/\.?0*$/, "");
|
|
112510
114080
|
}
|
|
@@ -112605,21 +114175,54 @@ Niivue.prototype.drawGraph = function() {
|
|
|
112605
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);
|
|
112606
114176
|
}
|
|
112607
114177
|
};
|
|
112608
|
-
|
|
114178
|
+
function isRadiological(mtx) {
|
|
114179
|
+
let vRight = fromValues(1, 0, 0, 0);
|
|
114180
|
+
let vRotated = create();
|
|
114181
|
+
transformMat4(vRotated, vRight, mtx);
|
|
114182
|
+
return vRotated[0];
|
|
114183
|
+
}
|
|
114184
|
+
Niivue.prototype.draw3D = function(leftTopWidthHeight = [0, 0, 0, 0], mvpMatrix = null, modelMatrix, normalMatrix, azimuth = null, elevation) {
|
|
114185
|
+
let isMosaic = azimuth !== null;
|
|
112609
114186
|
this.setPivot3D();
|
|
114187
|
+
if (!isMosaic) {
|
|
114188
|
+
azimuth = this.scene.renderAzimuth;
|
|
114189
|
+
elevation = this.scene.renderElevation;
|
|
114190
|
+
}
|
|
112610
114191
|
let gl = this.gl;
|
|
112611
|
-
|
|
114192
|
+
if (mvpMatrix === null)
|
|
114193
|
+
[mvpMatrix, modelMatrix, normalMatrix] = this.calculateMvpMatrix(null, leftTopWidthHeight, azimuth, elevation);
|
|
114194
|
+
if (leftTopWidthHeight[2] === 0 || leftTopWidthHeight[3] === 0) {
|
|
114195
|
+
leftTopWidthHeight = [0, 0, gl.canvas.width, gl.canvas.height];
|
|
114196
|
+
this.screenSlices.push({
|
|
114197
|
+
leftTopWidthHeight,
|
|
114198
|
+
axCorSag: this.sliceTypeRender,
|
|
114199
|
+
sliceFrac: 0,
|
|
114200
|
+
AxyzMxy: [],
|
|
114201
|
+
leftTopMM: [],
|
|
114202
|
+
fovMM: [isRadiological(modelMatrix), 0]
|
|
114203
|
+
});
|
|
114204
|
+
} else {
|
|
114205
|
+
this.screenSlices.push({
|
|
114206
|
+
leftTopWidthHeight: leftTopWidthHeight.slice(),
|
|
114207
|
+
axCorSag: this.sliceTypeRender,
|
|
114208
|
+
sliceFrac: 0,
|
|
114209
|
+
AxyzMxy: [],
|
|
114210
|
+
leftTopMM: [],
|
|
114211
|
+
fovMM: [isRadiological(modelMatrix), 0]
|
|
114212
|
+
});
|
|
114213
|
+
leftTopWidthHeight[1] = gl.canvas.height - leftTopWidthHeight[3] - leftTopWidthHeight[1];
|
|
114214
|
+
}
|
|
114215
|
+
gl.viewport(leftTopWidthHeight[0], leftTopWidthHeight[1], leftTopWidthHeight[2], leftTopWidthHeight[3]);
|
|
112612
114216
|
gl.enable(gl.DEPTH_TEST);
|
|
112613
114217
|
gl.depthFunc(gl.ALWAYS);
|
|
112614
114218
|
gl.clearDepth(0);
|
|
112615
114219
|
if (this.volumes.length === 0) {
|
|
112616
114220
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
112617
|
-
this.drawMesh3D(true, 1);
|
|
114221
|
+
this.drawMesh3D(true, 1, mvpMatrix, modelMatrix, normalMatrix);
|
|
114222
|
+
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
|
112618
114223
|
return;
|
|
112619
114224
|
}
|
|
112620
|
-
|
|
112621
|
-
[mvpMatrix, modelMatrix, normalMatrix] = this.calculateMvpMatrix(this.volumeObject3D);
|
|
112622
|
-
const rayDir = this.calculateRayDirection();
|
|
114225
|
+
const rayDir = this.calculateRayDirection(azimuth, elevation);
|
|
112623
114226
|
let object3D = this.volumeObject3D;
|
|
112624
114227
|
if (this.scene.mouseDepthPicker) {
|
|
112625
114228
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
@@ -112637,8 +114240,8 @@ Niivue.prototype.draw3D = function() {
|
|
|
112637
114240
|
gl.drawElements(object3D.mode, object3D.indexCount, gl.UNSIGNED_SHORT, 0);
|
|
112638
114241
|
gl.bindVertexArray(this.unusedVAO);
|
|
112639
114242
|
}
|
|
112640
|
-
const pixelX = this.mousePos[0] *
|
|
112641
|
-
const pixelY = gl.canvas.height - this.mousePos[1] *
|
|
114243
|
+
const pixelX = this.mousePos[0] * leftTopWidthHeight[2] / leftTopWidthHeight[2];
|
|
114244
|
+
const pixelY = gl.canvas.height - this.mousePos[1] * leftTopWidthHeight[3] / leftTopWidthHeight[3] - 1;
|
|
112642
114245
|
const rgbaPixel = new Uint8Array(4);
|
|
112643
114246
|
gl.readPixels(pixelX, pixelY, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, rgbaPixel);
|
|
112644
114247
|
this.selectedObjectId = rgbaPixel[3];
|
|
@@ -112662,22 +114265,25 @@ Niivue.prototype.draw3D = function() {
|
|
|
112662
114265
|
gl.drawElements(object3D.mode, object3D.indexCount, gl.UNSIGNED_SHORT, 0);
|
|
112663
114266
|
gl.bindVertexArray(this.unusedVAO);
|
|
112664
114267
|
}
|
|
112665
|
-
|
|
112666
|
-
|
|
112667
|
-
this.drawMesh3D(
|
|
112668
|
-
this.
|
|
114268
|
+
if (!isMosaic)
|
|
114269
|
+
this.drawCrosshairs3D(true, 1, mvpMatrix);
|
|
114270
|
+
this.drawMesh3D(true, 1, mvpMatrix, modelMatrix, normalMatrix);
|
|
114271
|
+
this.drawMesh3D(false, 0.02, mvpMatrix, modelMatrix, normalMatrix);
|
|
114272
|
+
if (!isMosaic)
|
|
114273
|
+
this.drawCrosshairs3D(false, 0.15, mvpMatrix);
|
|
114274
|
+
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
|
112669
114275
|
let posString = "azimuth: " + this.scene.renderAzimuth.toFixed(0) + " elevation: " + this.scene.renderElevation.toFixed(0);
|
|
112670
114276
|
this.drawGraph();
|
|
112671
114277
|
this.readyForSync = true;
|
|
112672
114278
|
this.sync();
|
|
112673
114279
|
return posString;
|
|
112674
114280
|
};
|
|
112675
|
-
Niivue.prototype.drawMesh3D = function(isDepthTest = true, alpha = 1) {
|
|
114281
|
+
Niivue.prototype.drawMesh3D = function(isDepthTest = true, alpha = 1, m, modelMtx, normMtx) {
|
|
112676
114282
|
if (this.meshes.length < 1)
|
|
112677
114283
|
return;
|
|
112678
114284
|
let gl = this.gl;
|
|
112679
|
-
|
|
112680
|
-
|
|
114285
|
+
if (!m)
|
|
114286
|
+
[m, modelMtx, normMtx] = this.calculateMvpMatrix(this.volumeObject3D, this.scene.renderAzimuth, this.scene.renderElevation);
|
|
112681
114287
|
gl.enable(gl.DEPTH_TEST);
|
|
112682
114288
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
112683
114289
|
gl.disable(gl.BLEND);
|
|
@@ -112729,7 +114335,7 @@ Niivue.prototype.drawMesh3D = function(isDepthTest = true, alpha = 1) {
|
|
|
112729
114335
|
gl.depthFunc(gl.ALWAYS);
|
|
112730
114336
|
this.readyForSync = true;
|
|
112731
114337
|
};
|
|
112732
|
-
Niivue.prototype.drawCrosshairs3D = function(isDepthTest = true, alpha = 1) {
|
|
114338
|
+
Niivue.prototype.drawCrosshairs3D = function(isDepthTest = true, alpha = 1, mvpMtx = null) {
|
|
112733
114339
|
if (!this.opts.show3Dcrosshair)
|
|
112734
114340
|
return;
|
|
112735
114341
|
let gl = this.gl;
|
|
@@ -112739,15 +114345,16 @@ Niivue.prototype.drawCrosshairs3D = function(isDepthTest = true, alpha = 1) {
|
|
|
112739
114345
|
gl.deleteBuffer(this.crosshairs3D.indexBuffer);
|
|
112740
114346
|
gl.deleteBuffer(this.crosshairs3D.vertexBuffer);
|
|
112741
114347
|
}
|
|
112742
|
-
let radius = Math.min(Math.min(this.back.pixDims[1], this.back.pixDims[2]), this.back.pixDims[3]);
|
|
114348
|
+
let radius = 0.5 * Math.min(Math.min(this.back.pixDims[1], this.back.pixDims[2]), this.back.pixDims[3]);
|
|
112743
114349
|
this.crosshairs3D = NiivueObject3D.generateCrosshairs(this.gl, 1, mm, this.volumeObject3D.extentsMin, this.volumeObject3D.extentsMax, radius);
|
|
112744
114350
|
this.crosshairs3D.mm = mm;
|
|
112745
114351
|
}
|
|
112746
114352
|
let crosshairsShader = this.surfaceShader;
|
|
112747
114353
|
crosshairsShader.use(this.gl);
|
|
112748
|
-
let
|
|
112749
|
-
|
|
112750
|
-
|
|
114354
|
+
let modelMtx, normMtx;
|
|
114355
|
+
if (mvpMtx == null)
|
|
114356
|
+
[mvpMtx, modelMtx, normMtx] = this.calculateMvpMatrix(this.crosshairs3D, this.scene.renderAzimuth, this.scene.renderElevation);
|
|
114357
|
+
gl.uniformMatrix4fv(crosshairsShader.uniforms["mvpMtx"], false, mvpMtx);
|
|
112751
114358
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.crosshairs3D.indexBuffer);
|
|
112752
114359
|
gl.enable(gl.DEPTH_TEST);
|
|
112753
114360
|
let color = [...this.opts.crosshairColor];
|
|
@@ -112809,69 +114416,77 @@ Niivue.prototype.moveCrosshairInVox = function(x2, y, z) {
|
|
|
112809
114416
|
};
|
|
112810
114417
|
Niivue.prototype.frac2mm = function(frac, volIdx = 0) {
|
|
112811
114418
|
let pos = fromValues(frac[0], frac[1], frac[2], 1);
|
|
112812
|
-
|
|
112813
|
-
let sform = clone$1(this.volumes[volIdx].matRAS);
|
|
112814
|
-
transpose(sform, sform);
|
|
112815
|
-
mul(pos, pos, dim);
|
|
112816
|
-
let shim = fromValues(-0.5, -0.5, -0.5, 0);
|
|
112817
|
-
add(pos, pos, shim);
|
|
112818
|
-
transformMat4(pos, pos, sform);
|
|
114419
|
+
transformMat4(pos, pos, this.volumes[volIdx].frac2mm);
|
|
112819
114420
|
return pos;
|
|
112820
114421
|
};
|
|
112821
|
-
Niivue.prototype.
|
|
112822
|
-
let
|
|
112823
|
-
|
|
112824
|
-
|
|
112825
|
-
|
|
112826
|
-
|
|
112827
|
-
|
|
112828
|
-
|
|
112829
|
-
|
|
112830
|
-
|
|
112831
|
-
|
|
112832
|
-
|
|
112833
|
-
|
|
112834
|
-
|
|
112835
|
-
|
|
112836
|
-
|
|
112837
|
-
|
|
112838
|
-
|
|
112839
|
-
|
|
112840
|
-
|
|
112841
|
-
|
|
112842
|
-
|
|
112843
|
-
|
|
112844
|
-
|
|
112845
|
-
|
|
112846
|
-
|
|
112847
|
-
|
|
112848
|
-
|
|
112849
|
-
|
|
112850
|
-
|
|
112851
|
-
|
|
112852
|
-
|
|
112853
|
-
|
|
112854
|
-
|
|
112855
|
-
|
|
112856
|
-
|
|
112857
|
-
|
|
112858
|
-
|
|
112859
|
-
|
|
112860
|
-
|
|
112861
|
-
|
|
114422
|
+
Niivue.prototype.screenXY2TextureFrac = function(x2, y, i2) {
|
|
114423
|
+
let texFrac = [-1, -1, -1];
|
|
114424
|
+
var axCorSag = this.screenSlices[i2].axCorSag;
|
|
114425
|
+
if (axCorSag > this.sliceTypeSagittal)
|
|
114426
|
+
return texFrac;
|
|
114427
|
+
var ltwh = this.screenSlices[i2].leftTopWidthHeight.slice();
|
|
114428
|
+
let isMirror = false;
|
|
114429
|
+
if (ltwh[2] < 0) {
|
|
114430
|
+
isMirror = true;
|
|
114431
|
+
ltwh[0] += ltwh[2];
|
|
114432
|
+
ltwh[2] = -ltwh[2];
|
|
114433
|
+
}
|
|
114434
|
+
var fracX = (x2 - ltwh[0]) / ltwh[2];
|
|
114435
|
+
if (isMirror)
|
|
114436
|
+
fracX = 1 - fracX;
|
|
114437
|
+
var fracY = 1 - (y - ltwh[1]) / ltwh[3];
|
|
114438
|
+
if (fracX < 0 || fracX > 1 || fracY < 0 || fracY > 1)
|
|
114439
|
+
return texFrac;
|
|
114440
|
+
if (this.screenSlices[i2].AxyzMxy.length > 4) {
|
|
114441
|
+
let xyzMM = [0, 0, 0];
|
|
114442
|
+
xyzMM[0] = this.screenSlices[i2].leftTopMM[0] + fracX * this.screenSlices[i2].fovMM[0];
|
|
114443
|
+
xyzMM[1] = this.screenSlices[i2].leftTopMM[1] + fracY * this.screenSlices[i2].fovMM[1];
|
|
114444
|
+
let v = this.screenSlices[i2].AxyzMxy;
|
|
114445
|
+
xyzMM[2] = v[2] + v[4] * (xyzMM[1] - v[1]) - v[3] * (xyzMM[0] - v[0]);
|
|
114446
|
+
if (axCorSag === this.sliceTypeCoronal)
|
|
114447
|
+
xyzMM = swizzleVec3(xyzMM, [0, 2, 1]);
|
|
114448
|
+
if (axCorSag === this.sliceTypeSagittal)
|
|
114449
|
+
xyzMM = swizzleVec3(xyzMM, [2, 0, 1]);
|
|
114450
|
+
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;
|
|
114453
|
+
texFrac[0] = xyz[0];
|
|
114454
|
+
texFrac[1] = xyz[1];
|
|
114455
|
+
texFrac[2] = xyz[2];
|
|
114456
|
+
} else {
|
|
114457
|
+
texFrac = this.scene.crosshairPos.slice();
|
|
114458
|
+
if (axCorSag === this.sliceTypeAxial) {
|
|
114459
|
+
texFrac[0] = fracX;
|
|
114460
|
+
texFrac[1] = fracY;
|
|
114461
|
+
}
|
|
114462
|
+
if (axCorSag === this.sliceTypeCoronal) {
|
|
114463
|
+
texFrac[0] = fracX;
|
|
114464
|
+
texFrac[2] = fracY;
|
|
114465
|
+
}
|
|
114466
|
+
if (axCorSag === this.sliceTypeSagittal) {
|
|
114467
|
+
texFrac[1] = fracX;
|
|
114468
|
+
texFrac[2] = fracY;
|
|
112862
114469
|
}
|
|
112863
114470
|
}
|
|
112864
|
-
return
|
|
114471
|
+
return texFrac;
|
|
114472
|
+
};
|
|
114473
|
+
Niivue.prototype.canvasPos2frac = function(canvasPos) {
|
|
114474
|
+
for (let i2 = 0; i2 < this.screenSlices.length; i2++) {
|
|
114475
|
+
let texFrac = this.screenXY2TextureFrac(canvasPos[0], canvasPos[1], i2);
|
|
114476
|
+
if (texFrac[0] >= 0)
|
|
114477
|
+
return texFrac;
|
|
114478
|
+
}
|
|
114479
|
+
return [-1, -1, -1];
|
|
112865
114480
|
};
|
|
112866
114481
|
Niivue.prototype.scaleSlice = function(w, h) {
|
|
112867
114482
|
let scalePix = this.gl.canvas.clientWidth / w;
|
|
112868
|
-
if (h * scalePix > this.
|
|
112869
|
-
scalePix = this.
|
|
114483
|
+
if (h * scalePix > this.effectiveCanvasHeight())
|
|
114484
|
+
scalePix = this.effectiveCanvasHeight() / h;
|
|
112870
114485
|
let wPix = w * scalePix;
|
|
112871
114486
|
let hPix = h * scalePix;
|
|
112872
114487
|
let leftTopWidthHeight = [
|
|
112873
114488
|
(this.gl.canvas.clientWidth - wPix) * 0.5,
|
|
112874
|
-
(this.
|
|
114489
|
+
(this.effectiveCanvasHeight() - hPix) * 0.5,
|
|
112875
114490
|
wPix,
|
|
112876
114491
|
hPix
|
|
112877
114492
|
];
|
|
@@ -112891,10 +114506,324 @@ Niivue.prototype.drawThumbnail = function() {
|
|
|
112891
114506
|
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
112892
114507
|
this.gl.bindVertexArray(this.unusedVAO);
|
|
112893
114508
|
};
|
|
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) {
|
|
114523
|
+
this.gl.bindVertexArray(this.genericVAO);
|
|
114524
|
+
this.lineShader.use(this.gl);
|
|
114525
|
+
this.gl.uniform4fv(this.lineShader.uniforms["lineColor"], this.opts.crosshairColor);
|
|
114526
|
+
this.gl.uniform2fv(this.lineShader.uniforms["canvasWidthHeight"], [
|
|
114527
|
+
this.gl.canvas.width,
|
|
114528
|
+
this.gl.canvas.height
|
|
114529
|
+
]);
|
|
114530
|
+
this.gl.uniform1f(this.lineShader.uniforms["thickness"], thickness);
|
|
114531
|
+
this.gl.uniform4fv(this.lineShader.uniforms["startXYendXY"], startXYendXY);
|
|
114532
|
+
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
114533
|
+
this.gl.bindVertexArray(this.unusedVAO);
|
|
114534
|
+
};
|
|
114535
|
+
Niivue.prototype.drawCrossLinesMM = function(sliceIndex, axCorSag, axiMM, corMM, sagMM) {
|
|
114536
|
+
if (sliceIndex < 0 || this.screenSlices.length <= sliceIndex)
|
|
114537
|
+
return;
|
|
114538
|
+
let tile = this.screenSlices[sliceIndex];
|
|
114539
|
+
let linesH = corMM.slice();
|
|
114540
|
+
let linesV = sagMM.slice();
|
|
114541
|
+
let thick = Math.max(1, this.opts.crosshairWidth);
|
|
114542
|
+
if (axCorSag === this.sliceTypeCoronal)
|
|
114543
|
+
linesH = axiMM.slice();
|
|
114544
|
+
if (axCorSag === this.sliceTypeSagittal) {
|
|
114545
|
+
linesH = axiMM.slice();
|
|
114546
|
+
linesV = corMM.slice();
|
|
114547
|
+
}
|
|
114548
|
+
function mm2screen(mm) {
|
|
114549
|
+
let screenXY = [0, 0];
|
|
114550
|
+
screenXY[0] = tile.leftTopWidthHeight[0] + (mm[0] - tile.leftTopMM[0]) / tile.fovMM[0] * tile.leftTopWidthHeight[2];
|
|
114551
|
+
screenXY[1] = tile.leftTopWidthHeight[1] + tile.leftTopWidthHeight[3] - (mm[1] - tile.leftTopMM[1]) / tile.fovMM[1] * tile.leftTopWidthHeight[3];
|
|
114552
|
+
return screenXY;
|
|
114553
|
+
}
|
|
114554
|
+
if (linesH.length > 0 && axCorSag === 0) {
|
|
114555
|
+
let fracZ = tile.sliceFrac;
|
|
114556
|
+
let dimV = 1;
|
|
114557
|
+
for (let i2 = 0; i2 < linesH.length; i2++) {
|
|
114558
|
+
let mmV = this.frac2mm([0.5, 0.5, 0.5]);
|
|
114559
|
+
mmV[dimV] = linesH[i2];
|
|
114560
|
+
let fracY = this.mm2frac(mmV);
|
|
114561
|
+
fracY = fracY[dimV];
|
|
114562
|
+
let left = this.frac2mm([0, fracY, fracZ]);
|
|
114563
|
+
left = swizzleVec3(left, [0, 1, 2]);
|
|
114564
|
+
let right = this.frac2mm([1, fracY, fracZ]);
|
|
114565
|
+
right = swizzleVec3(right, [0, 1, 2]);
|
|
114566
|
+
left = mm2screen(left);
|
|
114567
|
+
right = mm2screen(right);
|
|
114568
|
+
this.drawLine2DX([left[0], left[1], right[0], right[1]], thick);
|
|
114569
|
+
}
|
|
114570
|
+
}
|
|
114571
|
+
if (linesH.length > 0 && axCorSag === 1) {
|
|
114572
|
+
let fracH = tile.sliceFrac;
|
|
114573
|
+
this.frac2mm([0.5, fracH, 0.5]);
|
|
114574
|
+
let dimV = 2;
|
|
114575
|
+
for (let i2 = 0; i2 < linesH.length; i2++) {
|
|
114576
|
+
let mmV = this.frac2mm([0.5, 0.5, 0.5]);
|
|
114577
|
+
mmV[dimV] = linesH[i2];
|
|
114578
|
+
let fracV = this.mm2frac(mmV);
|
|
114579
|
+
fracV = fracV[dimV];
|
|
114580
|
+
let left = this.frac2mm([0, fracH, fracV]);
|
|
114581
|
+
left = swizzleVec3(left, [0, 2, 1]);
|
|
114582
|
+
let right = this.frac2mm([1, fracH, fracV]);
|
|
114583
|
+
right = swizzleVec3(right, [0, 2, 1]);
|
|
114584
|
+
left = mm2screen(left);
|
|
114585
|
+
right = mm2screen(right);
|
|
114586
|
+
this.drawLine2DX([left[0], left[1], right[0], right[1]], thick);
|
|
114587
|
+
}
|
|
114588
|
+
}
|
|
114589
|
+
if (linesH.length > 0 && axCorSag === 2) {
|
|
114590
|
+
let fracX = tile.sliceFrac;
|
|
114591
|
+
let dimV = 2;
|
|
114592
|
+
for (let i2 = 0; i2 < linesH.length; i2++) {
|
|
114593
|
+
let mmV = this.frac2mm([0.5, 0.5, 0.5]);
|
|
114594
|
+
mmV[dimV] = linesH[i2];
|
|
114595
|
+
let fracZ = this.mm2frac(mmV);
|
|
114596
|
+
fracZ = fracZ[dimV];
|
|
114597
|
+
let left = this.frac2mm([fracX, 0, fracZ]);
|
|
114598
|
+
left = swizzleVec3(left, [1, 2, 0]);
|
|
114599
|
+
let right = this.frac2mm([fracX, 1, fracZ]);
|
|
114600
|
+
right = swizzleVec3(right, [1, 2, 0]);
|
|
114601
|
+
left = mm2screen(left);
|
|
114602
|
+
right = mm2screen(right);
|
|
114603
|
+
this.drawLine2DX([left[0], left[1], right[0], right[1]], thick);
|
|
114604
|
+
}
|
|
114605
|
+
}
|
|
114606
|
+
if (linesV.length > 0 && axCorSag === 0) {
|
|
114607
|
+
let fracZ = tile.sliceFrac;
|
|
114608
|
+
let dimH = 0;
|
|
114609
|
+
for (let i2 = 0; i2 < linesV.length; i2++) {
|
|
114610
|
+
let mm = this.frac2mm([0.5, 0.5, 0.5]);
|
|
114611
|
+
mm[dimH] = linesV[i2];
|
|
114612
|
+
let frac = this.mm2frac(mm);
|
|
114613
|
+
frac = frac[dimH];
|
|
114614
|
+
let left = this.frac2mm([frac, 0, fracZ]);
|
|
114615
|
+
left = swizzleVec3(left, [0, 1, 2]);
|
|
114616
|
+
let right = this.frac2mm([frac, 1, fracZ]);
|
|
114617
|
+
right = swizzleVec3(right, [0, 1, 2]);
|
|
114618
|
+
left = mm2screen(left);
|
|
114619
|
+
right = mm2screen(right);
|
|
114620
|
+
this.drawLine2DX([left[0], left[1], right[0], right[1]], thick);
|
|
114621
|
+
}
|
|
114622
|
+
}
|
|
114623
|
+
if (linesV.length > 0 && axCorSag === 1) {
|
|
114624
|
+
let fracY = tile.sliceFrac;
|
|
114625
|
+
let dimH = 0;
|
|
114626
|
+
for (let i2 = 0; i2 < linesV.length; i2++) {
|
|
114627
|
+
let mm = this.frac2mm([0.5, 0.5, 0.5]);
|
|
114628
|
+
mm[dimH] = linesV[i2];
|
|
114629
|
+
let frac = this.mm2frac(mm);
|
|
114630
|
+
frac = frac[dimH];
|
|
114631
|
+
let left = this.frac2mm([frac, fracY, 0]);
|
|
114632
|
+
left = swizzleVec3(left, [0, 2, 1]);
|
|
114633
|
+
let right = this.frac2mm([frac, fracY, 1]);
|
|
114634
|
+
right = swizzleVec3(right, [0, 2, 1]);
|
|
114635
|
+
left = mm2screen(left);
|
|
114636
|
+
right = mm2screen(right);
|
|
114637
|
+
this.drawLine2DX([left[0], left[1], right[0], right[1]], thick);
|
|
114638
|
+
}
|
|
114639
|
+
}
|
|
114640
|
+
if (linesV.length > 0 && axCorSag === 2) {
|
|
114641
|
+
let fracX = tile.sliceFrac;
|
|
114642
|
+
let dimH = 1;
|
|
114643
|
+
for (let i2 = 0; i2 < linesV.length; i2++) {
|
|
114644
|
+
let mm = this.frac2mm([0.5, 0.5, 0.5]);
|
|
114645
|
+
mm[dimH] = linesV[i2];
|
|
114646
|
+
let frac = this.mm2frac(mm);
|
|
114647
|
+
frac = frac[dimH];
|
|
114648
|
+
let left = this.frac2mm([fracX, frac, 0]);
|
|
114649
|
+
left = swizzleVec3(left, [1, 2, 0]);
|
|
114650
|
+
let right = this.frac2mm([fracX, frac, 1]);
|
|
114651
|
+
right = swizzleVec3(right, [1, 2, 0]);
|
|
114652
|
+
left = mm2screen(left);
|
|
114653
|
+
right = mm2screen(right);
|
|
114654
|
+
this.drawLine2DX([left[0], left[1], right[0], right[1]], thick);
|
|
114655
|
+
}
|
|
114656
|
+
}
|
|
114657
|
+
};
|
|
114658
|
+
Niivue.prototype.drawCrossLines = function(sliceIndex, axCorSag, axiMM, corMM, sagMM) {
|
|
114659
|
+
if (sliceIndex < 0 || this.screenSlices.length <= sliceIndex)
|
|
114660
|
+
return;
|
|
114661
|
+
if (this.opts.isSliceMM)
|
|
114662
|
+
return this.drawCrossLinesMM(sliceIndex, axCorSag, axiMM, corMM, sagMM);
|
|
114663
|
+
let tile = this.screenSlices[sliceIndex];
|
|
114664
|
+
let linesH = corMM.slice();
|
|
114665
|
+
let linesV = sagMM.slice();
|
|
114666
|
+
Math.max(1, this.opts.crosshairWidth);
|
|
114667
|
+
if (axCorSag === this.sliceTypeCoronal)
|
|
114668
|
+
linesH = axiMM.slice();
|
|
114669
|
+
if (axCorSag === this.sliceTypeSagittal) {
|
|
114670
|
+
linesH = axiMM.slice();
|
|
114671
|
+
linesV = corMM.slice();
|
|
114672
|
+
}
|
|
114673
|
+
if (linesH.length > 0) {
|
|
114674
|
+
let LTWH = tile.leftTopWidthHeight.slice();
|
|
114675
|
+
let sliceDim = 2;
|
|
114676
|
+
let mm = this.frac2mm([0.5, 0.5, 0.5]);
|
|
114677
|
+
for (let i2 = 0; i2 < linesH.length; i2++) {
|
|
114678
|
+
mm[sliceDim] = linesH[i2];
|
|
114679
|
+
let frac = this.mm2frac(mm);
|
|
114680
|
+
this.drawLine([
|
|
114681
|
+
LTWH[0],
|
|
114682
|
+
LTWH[1] + LTWH[3] - frac[sliceDim] * LTWH[3],
|
|
114683
|
+
LTWH[2],
|
|
114684
|
+
1
|
|
114685
|
+
]);
|
|
114686
|
+
}
|
|
114687
|
+
}
|
|
114688
|
+
if (linesV.length > 0) {
|
|
114689
|
+
let LTWH = tile.leftTopWidthHeight.slice();
|
|
114690
|
+
let isRadiolgical = tile.fovMM[0] < 0;
|
|
114691
|
+
let sliceDim = 0;
|
|
114692
|
+
if (axCorSag === this.sliceTypeSagittal)
|
|
114693
|
+
sliceDim = 1;
|
|
114694
|
+
let mm = this.frac2mm([0.5, 0.5, 0.5]);
|
|
114695
|
+
for (let i2 = 0; i2 < linesV.length; i2++) {
|
|
114696
|
+
mm[sliceDim] = linesV[i2];
|
|
114697
|
+
let frac = this.mm2frac(mm);
|
|
114698
|
+
if (isRadiolgical)
|
|
114699
|
+
this.drawLine([
|
|
114700
|
+
LTWH[0] + (LTWH[2] - frac[sliceDim] * LTWH[2]),
|
|
114701
|
+
LTWH[1],
|
|
114702
|
+
1,
|
|
114703
|
+
LTWH[3]
|
|
114704
|
+
]);
|
|
114705
|
+
else
|
|
114706
|
+
this.drawLine([
|
|
114707
|
+
LTWH[0] + frac[sliceDim] * LTWH[2],
|
|
114708
|
+
LTWH[1],
|
|
114709
|
+
1,
|
|
114710
|
+
LTWH[3]
|
|
114711
|
+
]);
|
|
114712
|
+
}
|
|
114713
|
+
}
|
|
114714
|
+
};
|
|
114715
|
+
Niivue.prototype.drawMosaic = function(mosaicStr) {
|
|
114716
|
+
if (this.volumes.length === 0) {
|
|
114717
|
+
console.log("Unable to draw mosaic until voxel-based image is loaded");
|
|
114718
|
+
return;
|
|
114719
|
+
}
|
|
114720
|
+
this.screenSlices = [];
|
|
114721
|
+
let fovRenderMM = this.screenFieldOfViewMM(this.sliceTypeAxial, true);
|
|
114722
|
+
let fovSliceMM = this.screenFieldOfViewMM(this.sliceTypeAxial);
|
|
114723
|
+
mosaicStr = mosaicStr.replaceAll(";", " ;").trim();
|
|
114724
|
+
let axiMM = [];
|
|
114725
|
+
let corMM = [];
|
|
114726
|
+
let sagMM = [];
|
|
114727
|
+
let items = mosaicStr.split(/\s+/);
|
|
114728
|
+
let scale2 = 1;
|
|
114729
|
+
let labelSize = this.opts.textHeight;
|
|
114730
|
+
for (let pass = 0; pass < 2; pass++) {
|
|
114731
|
+
let isRender = false;
|
|
114732
|
+
let isCrossLines = false;
|
|
114733
|
+
isRender = false;
|
|
114734
|
+
let rowHt = 0;
|
|
114735
|
+
let left = 0;
|
|
114736
|
+
let top = 0;
|
|
114737
|
+
let mxRowWid = 0;
|
|
114738
|
+
let isLabel = false;
|
|
114739
|
+
let axCorSag = this.sliceTypeAxial;
|
|
114740
|
+
for (let i2 = 0; i2 < items.length; i2++) {
|
|
114741
|
+
let item = items[i2];
|
|
114742
|
+
if (item.includes("X")) {
|
|
114743
|
+
isCrossLines = true;
|
|
114744
|
+
continue;
|
|
114745
|
+
}
|
|
114746
|
+
if (item.includes("L")) {
|
|
114747
|
+
isLabel = !item.includes("-");
|
|
114748
|
+
continue;
|
|
114749
|
+
}
|
|
114750
|
+
if (item.includes("V") || item.includes("H")) {
|
|
114751
|
+
i2++;
|
|
114752
|
+
continue;
|
|
114753
|
+
}
|
|
114754
|
+
if (item.includes("A"))
|
|
114755
|
+
axCorSag = this.sliceTypeAxial;
|
|
114756
|
+
if (item.includes("C"))
|
|
114757
|
+
axCorSag = this.sliceTypeCoronal;
|
|
114758
|
+
if (item.includes("S"))
|
|
114759
|
+
axCorSag = this.sliceTypeSagittal;
|
|
114760
|
+
if (item.includes("R"))
|
|
114761
|
+
isRender = true;
|
|
114762
|
+
if (item.includes(";")) {
|
|
114763
|
+
top += rowHt;
|
|
114764
|
+
mxRowWid = Math.max(mxRowWid, left);
|
|
114765
|
+
rowHt = 0;
|
|
114766
|
+
left = 0;
|
|
114767
|
+
}
|
|
114768
|
+
let sliceMM = parseFloat(item, NaN);
|
|
114769
|
+
if (isNaN(sliceMM))
|
|
114770
|
+
continue;
|
|
114771
|
+
let w = 0;
|
|
114772
|
+
let h = 0;
|
|
114773
|
+
let fov = fovSliceMM;
|
|
114774
|
+
if (isRender)
|
|
114775
|
+
fov = fovRenderMM;
|
|
114776
|
+
if (axCorSag === this.sliceTypeSagittal)
|
|
114777
|
+
w = fov[1];
|
|
114778
|
+
else
|
|
114779
|
+
w = fov[0];
|
|
114780
|
+
if (axCorSag === this.sliceTypeAxial)
|
|
114781
|
+
h = fov[1];
|
|
114782
|
+
else
|
|
114783
|
+
h = fov[2];
|
|
114784
|
+
if (pass === 0) {
|
|
114785
|
+
if (!isRender) {
|
|
114786
|
+
if (axCorSag === this.sliceTypeAxial)
|
|
114787
|
+
axiMM.push(sliceMM);
|
|
114788
|
+
if (axCorSag === this.sliceTypeCoronal)
|
|
114789
|
+
corMM.push(sliceMM);
|
|
114790
|
+
if (axCorSag === this.sliceTypeSagittal)
|
|
114791
|
+
sagMM.push(sliceMM);
|
|
114792
|
+
}
|
|
114793
|
+
} else {
|
|
114794
|
+
let ltwh = [scale2 * left, scale2 * top, scale2 * w, scale2 * h];
|
|
114795
|
+
this.opts.textHeight = isLabel ? labelSize : 0;
|
|
114796
|
+
if (isRender) {
|
|
114797
|
+
let inf = sliceMM < 0 ? -Infinity : Infinity;
|
|
114798
|
+
if (Object.is(sliceMM, -0))
|
|
114799
|
+
inf = -Infinity;
|
|
114800
|
+
this.draw2DMM(ltwh, axCorSag, inf, isLabel);
|
|
114801
|
+
} else
|
|
114802
|
+
this.draw2D(ltwh, axCorSag, sliceMM, isLabel);
|
|
114803
|
+
if (isCrossLines) {
|
|
114804
|
+
this.drawCrossLines(this.screenSlices.length - 1, axCorSag, axiMM, corMM, sagMM);
|
|
114805
|
+
}
|
|
114806
|
+
isRender = false;
|
|
114807
|
+
isCrossLines = false;
|
|
114808
|
+
}
|
|
114809
|
+
left += w;
|
|
114810
|
+
rowHt = Math.max(rowHt, h);
|
|
114811
|
+
}
|
|
114812
|
+
top += rowHt;
|
|
114813
|
+
mxRowWid = Math.max(mxRowWid, left);
|
|
114814
|
+
if (mxRowWid <= 0 || top <= 0)
|
|
114815
|
+
break;
|
|
114816
|
+
let scaleW = this.gl.canvas.width / mxRowWid;
|
|
114817
|
+
let scaleH = this.effectiveCanvasHeight() / top;
|
|
114818
|
+
scale2 = Math.min(scaleW, scaleH);
|
|
114819
|
+
}
|
|
114820
|
+
this.opts.textHeight = labelSize;
|
|
114821
|
+
};
|
|
112894
114822
|
Niivue.prototype.drawScene = function() {
|
|
112895
114823
|
if (!this.initialized) {
|
|
112896
114824
|
return;
|
|
112897
114825
|
}
|
|
114826
|
+
this.colorbarHeight = 0;
|
|
112898
114827
|
this.gl.clearColor(this.opts.backColor[0], this.opts.backColor[1], this.opts.backColor[2], this.opts.backColor[3]);
|
|
112899
114828
|
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
|
|
112900
114829
|
if (this.bmpTexture) {
|
|
@@ -112904,6 +114833,7 @@ Niivue.prototype.drawScene = function() {
|
|
|
112904
114833
|
let posString = "";
|
|
112905
114834
|
if (this.volumes.length === 0 || typeof this.volumes[0].dims === "undefined") {
|
|
112906
114835
|
if (this.meshes.length > 0) {
|
|
114836
|
+
this.screenSlices = [];
|
|
112907
114837
|
this.sliceType = this.sliceTypeRender;
|
|
112908
114838
|
return this.draw3D();
|
|
112909
114839
|
}
|
|
@@ -112912,64 +114842,90 @@ Niivue.prototype.drawScene = function() {
|
|
|
112912
114842
|
}
|
|
112913
114843
|
if (!this.back.hasOwnProperty("dims"))
|
|
112914
114844
|
return;
|
|
112915
|
-
if (this.
|
|
112916
|
-
|
|
112917
|
-
|
|
112918
|
-
|
|
112919
|
-
|
|
112920
|
-
|
|
112921
|
-
|
|
112922
|
-
|
|
112923
|
-
|
|
112924
|
-
|
|
112925
|
-
|
|
112926
|
-
|
|
112927
|
-
|
|
112928
|
-
|
|
114845
|
+
if (this.isDragging && this.scene.clipPlaneDepthAziElev[0] < 1.8 && this.inRenderTile(this.dragStart[0], this.dragStart[1]) >= 0) {
|
|
114846
|
+
let x2 = this.dragStart[0] - this.dragEnd[0];
|
|
114847
|
+
let y = this.dragStart[1] - this.dragEnd[1];
|
|
114848
|
+
let depthAziElev = this.dragClipPlaneStartDepthAziElev.slice();
|
|
114849
|
+
depthAziElev[1] -= x2;
|
|
114850
|
+
depthAziElev[1] = depthAziElev[1] % 360;
|
|
114851
|
+
depthAziElev[2] += y;
|
|
114852
|
+
if (depthAziElev[1] !== this.scene.clipPlaneDepthAziElev[1] || depthAziElev[2] !== this.scene.clipPlaneDepthAziElev[2]) {
|
|
114853
|
+
this.scene.clipPlaneDepthAziElev = depthAziElev;
|
|
114854
|
+
return this.setClipPlane(this.scene.clipPlaneDepthAziElev);
|
|
114855
|
+
}
|
|
114856
|
+
}
|
|
114857
|
+
if (this.sliceMosaicString.length < 1 && this.sliceType === this.sliceTypeRender) {
|
|
114858
|
+
this.screenSlices = [];
|
|
114859
|
+
this.draw3D();
|
|
114860
|
+
if (this.opts.isColorbar)
|
|
114861
|
+
this.drawColorbar();
|
|
114862
|
+
return;
|
|
112929
114863
|
}
|
|
112930
|
-
|
|
112931
|
-
|
|
112932
|
-
this.
|
|
112933
|
-
|
|
112934
|
-
let leftTopWidthHeight = this.scaleSlice(volScale[0], volScale[1]);
|
|
112935
|
-
this.draw2D(leftTopWidthHeight, 0);
|
|
112936
|
-
} else if (this.sliceType === this.sliceTypeCoronal) {
|
|
112937
|
-
let leftTopWidthHeight = this.scaleSlice(volScale[0], volScale[2]);
|
|
112938
|
-
this.draw2D(leftTopWidthHeight, 1);
|
|
112939
|
-
} else if (this.sliceType === this.sliceTypeSagittal) {
|
|
112940
|
-
let leftTopWidthHeight = this.scaleSlice(volScale[1], volScale[2]);
|
|
112941
|
-
this.draw2D(leftTopWidthHeight, 2);
|
|
114864
|
+
if (this.opts.isColorbar)
|
|
114865
|
+
this.reserveColorbarPanel();
|
|
114866
|
+
if (this.sliceMosaicString.length > 0) {
|
|
114867
|
+
this.drawMosaic(this.sliceMosaicString);
|
|
112942
114868
|
} else {
|
|
112943
|
-
|
|
112944
|
-
|
|
112945
|
-
|
|
112946
|
-
|
|
112947
|
-
if (
|
|
112948
|
-
|
|
112949
|
-
|
|
112950
|
-
|
|
112951
|
-
this.draw2D([ltwh3x1[0], ltwh3x1[1], wX1, hY1], 0);
|
|
112952
|
-
this.draw2D([ltwh3x1[0] + wX1, ltwh3x1[1], wX1, hZ1], 1);
|
|
112953
|
-
this.draw2D([ltwh3x1[0] + wX1 + wX1, ltwh3x1[1], hY1, hZ1], 2);
|
|
114869
|
+
this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
|
|
114870
|
+
this.screenSlices = [];
|
|
114871
|
+
if (this.sliceType === this.sliceTypeAxial) {
|
|
114872
|
+
this.draw2D([0, 0, 0, 0], 0);
|
|
114873
|
+
} else if (this.sliceType === this.sliceTypeCoronal) {
|
|
114874
|
+
this.draw2D([0, 0, 0, 0], 1);
|
|
114875
|
+
} else if (this.sliceType === this.sliceTypeSagittal) {
|
|
114876
|
+
this.draw2D([0, 0, 0, 0], 2);
|
|
112954
114877
|
} else {
|
|
112955
|
-
let
|
|
112956
|
-
let
|
|
112957
|
-
let
|
|
112958
|
-
this.
|
|
112959
|
-
|
|
112960
|
-
this.
|
|
112961
|
-
|
|
112962
|
-
if (!this.graph.autoSizeMultiplanar) {
|
|
112963
|
-
|
|
112964
|
-
|
|
112965
|
-
|
|
112966
|
-
|
|
112967
|
-
|
|
112968
|
-
|
|
114878
|
+
let { volScale, vox, longestAxis } = this.sliceScale();
|
|
114879
|
+
let ltwh = this.scaleSlice(volScale[0] + volScale[1], volScale[1] + volScale[2]);
|
|
114880
|
+
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]));
|
|
114882
|
+
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);
|
|
114884
|
+
let wX1 = ltwh3x1[2] * volScale[0] / (volScale[0] + volScale[0] + volScale[1]);
|
|
114885
|
+
if (wX1 > wX && !this.graph.autoSizeMultiplanar) {
|
|
114886
|
+
let pixScale = wX1 / volScale[0];
|
|
114887
|
+
let hY1 = volScale[1] * pixScale;
|
|
114888
|
+
let hZ1 = volScale[2] * pixScale;
|
|
114889
|
+
if (ltwh3x1[3] === ltwh4x1[3]) {
|
|
114890
|
+
ltwh3x1 = ltwh4x1;
|
|
114891
|
+
this.draw3D([
|
|
114892
|
+
ltwh3x1[0] + wX1 + wX1 + hY1,
|
|
114893
|
+
ltwh3x1[1],
|
|
114894
|
+
ltwh4x1[3],
|
|
114895
|
+
ltwh4x1[3]
|
|
114896
|
+
]);
|
|
114897
|
+
}
|
|
114898
|
+
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);
|
|
114901
|
+
} else {
|
|
114902
|
+
let wY = ltwh[2] - wX;
|
|
114903
|
+
let hY = ltwh[3] * volScale[1] / (volScale[1] + volScale[2]);
|
|
114904
|
+
let hZ = ltwh[3] - hY;
|
|
114905
|
+
this.draw2D([ltwh[0], ltwh[1] + hZ, wX, hY], 0);
|
|
114906
|
+
this.draw2D([ltwh[0], ltwh[1], wX, hZ], 1);
|
|
114907
|
+
this.draw2D([ltwh[0] + wX, ltwh[1], wY, hZ], 2);
|
|
114908
|
+
if (!this.graph.autoSizeMultiplanar)
|
|
114909
|
+
this.draw3D([ltwh[0] + wX, ltwh[1] + hZ, wY, hY]);
|
|
112969
114910
|
}
|
|
112970
114911
|
}
|
|
112971
114912
|
}
|
|
112972
|
-
if (this.
|
|
114913
|
+
if (this.opts.isRuler)
|
|
114914
|
+
this.drawRuler();
|
|
114915
|
+
if (this.opts.isColorbar)
|
|
114916
|
+
this.drawColorbar();
|
|
114917
|
+
if (this.isDragging) {
|
|
114918
|
+
if (this.opts.isDragShowsMeasurementTool) {
|
|
114919
|
+
this.drawMeasurementTool([
|
|
114920
|
+
this.dragStart[0],
|
|
114921
|
+
this.dragStart[1],
|
|
114922
|
+
this.dragEnd[0],
|
|
114923
|
+
this.dragEnd[1]
|
|
114924
|
+
]);
|
|
114925
|
+
return;
|
|
114926
|
+
}
|
|
114927
|
+
if (this.inRenderTile(this.dragStart[0], this.dragStart[1]) >= 0)
|
|
114928
|
+
return;
|
|
112973
114929
|
let width = Math.abs(this.dragStart[0] - this.dragEnd[0]);
|
|
112974
114930
|
let height = Math.abs(this.dragStart[1] - this.dragEnd[1]);
|
|
112975
114931
|
this.drawSelectionBox([
|