@niivue/niivue 0.24.3 → 0.26.2
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 +2796 -520
- package/dist/niivue.umd.js +303 -142
- package/package.json +1 -1
- package/src/colortables.js +1 -1
- package/src/index.html +3 -2
- package/src/indexDraw.html +7 -2
- package/src/indexMM.html +229 -0
- package/src/indexModulate.html +223 -0
- package/src/niivue.js +1657 -575
- package/src/nvimage.js +125 -26
- package/src/shader-srcs.js +209 -59
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];
|
|
@@ -364,6 +364,26 @@ function translate(out, a, v) {
|
|
|
364
364
|
}
|
|
365
365
|
return out;
|
|
366
366
|
}
|
|
367
|
+
function scale$1(out, a, v) {
|
|
368
|
+
var x2 = v[0], y = v[1], z = v[2];
|
|
369
|
+
out[0] = a[0] * x2;
|
|
370
|
+
out[1] = a[1] * x2;
|
|
371
|
+
out[2] = a[2] * x2;
|
|
372
|
+
out[3] = a[3] * x2;
|
|
373
|
+
out[4] = a[4] * y;
|
|
374
|
+
out[5] = a[5] * y;
|
|
375
|
+
out[6] = a[6] * y;
|
|
376
|
+
out[7] = a[7] * y;
|
|
377
|
+
out[8] = a[8] * z;
|
|
378
|
+
out[9] = a[9] * z;
|
|
379
|
+
out[10] = a[10] * z;
|
|
380
|
+
out[11] = a[11] * z;
|
|
381
|
+
out[12] = a[12];
|
|
382
|
+
out[13] = a[13];
|
|
383
|
+
out[14] = a[14];
|
|
384
|
+
out[15] = a[15];
|
|
385
|
+
return out;
|
|
386
|
+
}
|
|
367
387
|
function rotateX(out, a, rad) {
|
|
368
388
|
var s = Math.sin(rad);
|
|
369
389
|
var c2 = Math.cos(rad);
|
|
@@ -500,7 +520,7 @@ function multiplyScalar(out, a, b) {
|
|
|
500
520
|
out[15] = a[15] * b;
|
|
501
521
|
return out;
|
|
502
522
|
}
|
|
503
|
-
var mul
|
|
523
|
+
var mul = multiply;
|
|
504
524
|
function create$1() {
|
|
505
525
|
var out = new ARRAY_TYPE(3);
|
|
506
526
|
if (ARRAY_TYPE != Float32Array) {
|
|
@@ -609,6 +629,7 @@ function angle(a, b) {
|
|
|
609
629
|
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
630
|
return Math.acos(Math.min(Math.max(cosine, -1), 1));
|
|
611
631
|
}
|
|
632
|
+
var sub = subtract;
|
|
612
633
|
var len = length$1;
|
|
613
634
|
(function() {
|
|
614
635
|
var vec = create$1();
|
|
@@ -662,13 +683,6 @@ function add(out, a, b) {
|
|
|
662
683
|
out[3] = a[3] + b[3];
|
|
663
684
|
return out;
|
|
664
685
|
}
|
|
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
686
|
function transformMat4(out, a, m) {
|
|
673
687
|
var x2 = a[0], y = a[1], z = a[2], w = a[3];
|
|
674
688
|
out[0] = m[0] * x2 + m[4] * y + m[8] * z + m[12] * w;
|
|
@@ -677,7 +691,6 @@ function transformMat4(out, a, m) {
|
|
|
677
691
|
out[3] = m[3] * x2 + m[7] * y + m[11] * z + m[15] * w;
|
|
678
692
|
return out;
|
|
679
693
|
}
|
|
680
|
-
var mul = multiply;
|
|
681
694
|
(function() {
|
|
682
695
|
var vec = create();
|
|
683
696
|
return function(a, stride, offset, count, fn, arg2) {
|
|
@@ -754,7 +767,20 @@ vec4 applyClip (vec3 dir, inout vec4 samplePos, inout float len, inout bool isCl
|
|
|
754
767
|
samplePos = vec4(samplePos.xyz+dir * disBackFace, disBackFace);
|
|
755
768
|
}
|
|
756
769
|
return samplePos;
|
|
757
|
-
}
|
|
770
|
+
}
|
|
771
|
+
float frac2ndc(vec3 frac) {
|
|
772
|
+
//https://stackoverflow.com/questions/7777913/how-to-render-depth-linearly-in-modern-opengl-with-gl-fragcoord-z-in-fragment-sh
|
|
773
|
+
vec4 pos = vec4(frac.xyz, 1.0); //fraction
|
|
774
|
+
vec4 dim = vec4(vec3(textureSize(volume, 0)), 1.0);
|
|
775
|
+
pos = pos * dim;
|
|
776
|
+
vec4 shim = vec4(-0.5, -0.5, -0.5, 0.0);
|
|
777
|
+
pos += shim;
|
|
778
|
+
vec4 mm = transpose(matRAS) * pos;
|
|
779
|
+
float z_ndc = (mvpMtx * vec4(mm.xyz, 1.0)).z;
|
|
780
|
+
return (z_ndc + 1.0) / 2.0;
|
|
781
|
+
|
|
782
|
+
}
|
|
783
|
+
`;
|
|
758
784
|
var fragRenderShader = `#version 300 es
|
|
759
785
|
#line 14
|
|
760
786
|
precision highp int;
|
|
@@ -772,20 +798,11 @@ uniform mat4 matRAS;
|
|
|
772
798
|
uniform vec4 clipPlaneColor;
|
|
773
799
|
in vec3 vColor;
|
|
774
800
|
out vec4 fColor;
|
|
775
|
-
` + kRenderFunc + `
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
pos = pos * dim;
|
|
781
|
-
vec4 shim = vec4(-0.5, -0.5, -0.5, 0.0);
|
|
782
|
-
pos += shim;
|
|
783
|
-
vec4 mm = transpose(matRAS) * pos;
|
|
784
|
-
float z_ndc = (mvpMtx * vec4(mm.xyz, 1.0)).z;
|
|
785
|
-
return (z_ndc + 1.0) / 2.0;
|
|
786
|
-
|
|
787
|
-
}
|
|
788
|
-
void main() {
|
|
801
|
+
` + kRenderFunc + `void main() {
|
|
802
|
+
if (fColor.x > 2.0) {
|
|
803
|
+
fColor = vec4(1.0, 0.0, 0.0, 0.5);
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
789
806
|
fColor = vec4(0.0,0.0,0.0,0.0);
|
|
790
807
|
vec4 clipPlaneColorX = clipPlaneColor;
|
|
791
808
|
//if (clipPlaneColor.a < 0.0)
|
|
@@ -821,19 +838,23 @@ void main() {
|
|
|
821
838
|
vec4 deltaDirFast = vec4(dir.xyz * stepSizeFast, stepSizeFast);
|
|
822
839
|
while (samplePos.a <= len) {
|
|
823
840
|
float val = texture(volume, samplePos.xyz).a;
|
|
824
|
-
|
|
841
|
+
if (val > 0.01) {
|
|
842
|
+
break;
|
|
843
|
+
}
|
|
825
844
|
samplePos += deltaDirFast; //advance ray position
|
|
826
845
|
}
|
|
827
846
|
if ((samplePos.a >= len) && ((overlays < 1.0) || (backgroundMasksOverlays > 0))) {
|
|
828
|
-
|
|
847
|
+
if (isClip){
|
|
829
848
|
fColor += clipPlaneColorX;
|
|
849
|
+
}
|
|
830
850
|
return;
|
|
831
851
|
}
|
|
832
852
|
fColor = vec4(1.0, 1.0, 1.0, 1.0);
|
|
833
853
|
//gl_FragDepth = frac2ndc(samplePos.xyz); //crude due to fast pass resolution
|
|
834
854
|
samplePos -= deltaDirFast;
|
|
835
|
-
|
|
855
|
+
if (samplePos.a < 0.0){
|
|
836
856
|
vec4 samplePos = vec4(start.xyz, 0.0); //ray position
|
|
857
|
+
}
|
|
837
858
|
//end: fast pass
|
|
838
859
|
vec4 colAcc = vec4(0.0,0.0,0.0,0.0);
|
|
839
860
|
vec4 firstHit = vec4(0.0,0.0,0.0,2.0 * lenNoClip);
|
|
@@ -844,25 +865,29 @@ void main() {
|
|
|
844
865
|
while (samplePos.a <= len) {
|
|
845
866
|
vec4 colorSample = texture(volume, samplePos.xyz);
|
|
846
867
|
samplePos += deltaDir; //advance ray position
|
|
847
|
-
if (colorSample.a
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
868
|
+
if (colorSample.a >= 0.01) {
|
|
869
|
+
if (firstHit.a > lenNoClip)
|
|
870
|
+
firstHit = samplePos;
|
|
871
|
+
backNearest = min(backNearest, samplePos.a);
|
|
872
|
+
colorSample.a = 1.0-pow((1.0 - colorSample.a), opacityCorrection);
|
|
873
|
+
colorSample.rgb *= colorSample.a;
|
|
874
|
+
colAcc= (1.0 - colAcc.a) * colorSample + colAcc;
|
|
875
|
+
if ( colAcc.a > earlyTermination )
|
|
876
|
+
break;
|
|
877
|
+
}
|
|
856
878
|
}
|
|
857
|
-
|
|
879
|
+
if (firstHit.a < len){
|
|
858
880
|
gl_FragDepth = frac2ndc(firstHit.xyz);
|
|
881
|
+
}
|
|
859
882
|
colAcc.a = (colAcc.a / earlyTermination) * backOpacity;
|
|
860
883
|
fColor = colAcc;
|
|
861
884
|
//if (isClip) //CR
|
|
862
885
|
if ((isColorPlaneInVolume) && (clipPos.a != samplePos.a) && (abs(firstHit.a - clipPos.a) < deltaDir.a))
|
|
863
886
|
fColor.rgb = mix(fColor.rgb, clipPlaneColorX.rgb, abs(clipPlaneColor.a));
|
|
864
887
|
//fColor.rgb = mix(fColor.rgb, clipPlaneColorX.rgb, clipPlaneColorX.a * 0.65);
|
|
865
|
-
|
|
888
|
+
if (overlays < 1.0) {
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
866
891
|
//overlay pass
|
|
867
892
|
len = lenNoClip;
|
|
868
893
|
samplePos = vec4(start.xyz, 0.0); //ray position
|
|
@@ -871,48 +896,56 @@ void main() {
|
|
|
871
896
|
deltaDirFast = vec4(dir.xyz * stepSizeFast, stepSizeFast);
|
|
872
897
|
while (samplePos.a <= len) {
|
|
873
898
|
float val = texture(overlay, samplePos.xyz).a;
|
|
874
|
-
|
|
899
|
+
if (val > 0.01) {
|
|
900
|
+
break;
|
|
901
|
+
}
|
|
875
902
|
samplePos += deltaDirFast; //advance ray position
|
|
876
903
|
}
|
|
877
904
|
if (samplePos.a >= len) {
|
|
878
|
-
|
|
905
|
+
if (isClip && (fColor.a == 0.0)) {
|
|
879
906
|
fColor += clipPlaneColorX;
|
|
907
|
+
}
|
|
880
908
|
return;
|
|
881
909
|
}
|
|
882
910
|
samplePos -= deltaDirFast;
|
|
883
|
-
|
|
911
|
+
if (samplePos.a < 0.0){
|
|
884
912
|
vec4 samplePos = vec4(start.xyz, 0.0); //ray position
|
|
913
|
+
}
|
|
885
914
|
//end: fast pass
|
|
886
915
|
float overFarthest = len;
|
|
887
916
|
colAcc = vec4(0.0, 0.0, 0.0, 0.0);
|
|
888
917
|
samplePos += deltaDir * ran; //jitter ray
|
|
889
918
|
vec4 overFirstHit = vec4(0.0,0.0,0.0,2.0 * len);
|
|
890
|
-
|
|
919
|
+
if (backgroundMasksOverlays > 0){
|
|
891
920
|
samplePos = firstHit;
|
|
921
|
+
}
|
|
892
922
|
while (samplePos.a <= len) {
|
|
893
923
|
vec4 colorSample = texture(overlay, samplePos.xyz);
|
|
894
924
|
samplePos += deltaDir; //advance ray position
|
|
895
|
-
if (colorSample.a
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
925
|
+
if (colorSample.a >= 0.01) {
|
|
926
|
+
if (overFirstHit.a > len)
|
|
927
|
+
overFirstHit = samplePos;
|
|
928
|
+
colorSample.a = 1.0-pow((1.0 - colorSample.a), opacityCorrection);
|
|
929
|
+
colorSample.rgb *= colorSample.a;
|
|
930
|
+
colAcc= (1.0 - colAcc.a) * colorSample + colAcc;
|
|
931
|
+
overFarthest = samplePos.a;
|
|
932
|
+
if ( colAcc.a > earlyTermination )
|
|
933
|
+
break;
|
|
934
|
+
}
|
|
904
935
|
}
|
|
905
936
|
if (samplePos.a >= len) {
|
|
906
|
-
|
|
937
|
+
if (isClip && (fColor.a == 0.0)){
|
|
907
938
|
fColor += clipPlaneColorX;
|
|
939
|
+
}
|
|
908
940
|
return;
|
|
909
941
|
}
|
|
910
942
|
//if (overFirstHit.a < len)
|
|
911
|
-
|
|
943
|
+
gl_FragDepth = frac2ndc(overFirstHit.xyz);
|
|
912
944
|
float overMix = colAcc.a;
|
|
913
945
|
float overlayDepth = 0.3;
|
|
914
|
-
|
|
946
|
+
if (fColor.a <= 0.0){
|
|
915
947
|
overMix = 1.0;
|
|
948
|
+
}
|
|
916
949
|
else if (((overFarthest) > backNearest)) {
|
|
917
950
|
float dx = (overFarthest - backNearest)/1.73;
|
|
918
951
|
dx = fColor.a * pow(dx, overlayDepth);
|
|
@@ -927,15 +960,22 @@ layout(location=0) in vec3 pos;
|
|
|
927
960
|
uniform int axCorSag;
|
|
928
961
|
uniform float slice;
|
|
929
962
|
uniform vec2 canvasWidthHeight;
|
|
963
|
+
uniform vec3 panXYscale;
|
|
930
964
|
uniform vec4 leftTopWidthHeight;
|
|
931
965
|
out vec3 texPos;
|
|
932
966
|
void main(void) {
|
|
933
967
|
//convert pixel x,y space 1..canvasWidth,1..canvasHeight to WebGL 1..-1,-1..1
|
|
934
968
|
vec2 frac;
|
|
935
|
-
|
|
936
|
-
frac.
|
|
969
|
+
vec3 vpos = pos;
|
|
970
|
+
frac.x = (leftTopWidthHeight.x + (vpos.x * leftTopWidthHeight.z)) / canvasWidthHeight.x; //0..1
|
|
971
|
+
frac.y = 1.0 - ((leftTopWidthHeight.y + ((1.0 - vpos.y) * leftTopWidthHeight.w)) / canvasWidthHeight.y); //1..0
|
|
972
|
+
//frac.x = pos.x; //0..1
|
|
973
|
+
//frac.y = pos.y; //1..0
|
|
974
|
+
|
|
937
975
|
frac = (frac * 2.0) - 1.0;
|
|
938
976
|
gl_Position = vec4(frac, 0.0, 1.0);
|
|
977
|
+
gl_Position.x += panXYscale.x;
|
|
978
|
+
gl_Position.y += panXYscale.y;
|
|
939
979
|
if (axCorSag == 1)
|
|
940
980
|
texPos = vec3(pos.x, slice, pos.y);
|
|
941
981
|
else if (axCorSag == 2)
|
|
@@ -984,7 +1024,68 @@ void main() {
|
|
|
984
1024
|
color.rgb = mix(color.rgb, ocolor.rgb, ocolor.a);
|
|
985
1025
|
color.a = aout;
|
|
986
1026
|
}`;
|
|
987
|
-
var
|
|
1027
|
+
var vertSliceMMShader = `#version 300 es
|
|
1028
|
+
#line 4
|
|
1029
|
+
layout(location=0) in vec3 pos;
|
|
1030
|
+
uniform int axCorSag;
|
|
1031
|
+
uniform mat4 mvpMtx;
|
|
1032
|
+
uniform mat4 frac2mm;
|
|
1033
|
+
uniform float slice;
|
|
1034
|
+
out vec3 texPos;
|
|
1035
|
+
void main(void) {
|
|
1036
|
+
texPos = vec3(pos.x, pos.y, slice);
|
|
1037
|
+
if (axCorSag > 1)
|
|
1038
|
+
texPos = vec3(slice, pos.x, pos.y);
|
|
1039
|
+
else if (axCorSag > 0)
|
|
1040
|
+
texPos = vec3(pos.x, slice, pos.y);
|
|
1041
|
+
vec4 mm = frac2mm * vec4(texPos, 1.0);
|
|
1042
|
+
//vec4 mm = vec4(texPos, 1.0) * frac2mm;
|
|
1043
|
+
gl_Position = mvpMtx * mm;
|
|
1044
|
+
//gl_Position = mm;
|
|
1045
|
+
}`;
|
|
1046
|
+
var fragSliceMMShader = `#version 300 es
|
|
1047
|
+
#line 228
|
|
1048
|
+
precision highp int;
|
|
1049
|
+
precision highp float;
|
|
1050
|
+
uniform highp sampler3D volume, overlay;
|
|
1051
|
+
uniform int backgroundMasksOverlays;
|
|
1052
|
+
uniform float overlays;
|
|
1053
|
+
uniform float opacity;
|
|
1054
|
+
uniform float drawOpacity;
|
|
1055
|
+
uniform highp sampler3D drawing;
|
|
1056
|
+
in vec3 texPos;
|
|
1057
|
+
out vec4 color;
|
|
1058
|
+
void main() {
|
|
1059
|
+
//color = vec4(1.0, 0.0, 1.0, 1.0);return;
|
|
1060
|
+
vec4 background = texture(volume, texPos);
|
|
1061
|
+
color = vec4(background.rgb, opacity);
|
|
1062
|
+
vec4 ocolor = vec4(0.0);
|
|
1063
|
+
if (overlays > 0.0) {
|
|
1064
|
+
ocolor = texture(overlay, texPos);
|
|
1065
|
+
}
|
|
1066
|
+
float draw = texture(drawing, texPos).r;
|
|
1067
|
+
if (draw > 0.0) {
|
|
1068
|
+
vec3 dcolor = vec3(0.0, 0.0, 0.0);
|
|
1069
|
+
if (draw >= (4.0/255.0))
|
|
1070
|
+
dcolor.rgb = vec3(draw,0.0,draw);
|
|
1071
|
+
else if (draw >= (3.0/255.0))
|
|
1072
|
+
dcolor.b = 1.0;
|
|
1073
|
+
else if (draw >= (2.0/255.0))
|
|
1074
|
+
dcolor.g = 1.0;
|
|
1075
|
+
else
|
|
1076
|
+
dcolor.r = 1.0;
|
|
1077
|
+
color.rgb = mix(color.rgb, dcolor, drawOpacity);
|
|
1078
|
+
color.a = max(drawOpacity, color.a);
|
|
1079
|
+
}
|
|
1080
|
+
if ((backgroundMasksOverlays > 0) && (background.a == 0.0))
|
|
1081
|
+
return;
|
|
1082
|
+
//float aout = ocolor.a + (1.0 - ocolor.a) * color.a;
|
|
1083
|
+
//if (aout <= 0.0) return;
|
|
1084
|
+
//color.rgb = ((ocolor.rgb * ocolor.a) + (color.rgb * color.a * (1.0 - ocolor.a))) / aout;
|
|
1085
|
+
color.rgb = mix(color.rgb, ocolor.rgb, ocolor.a);
|
|
1086
|
+
color.a = 1.0;
|
|
1087
|
+
}`;
|
|
1088
|
+
var fragRectShader = `#version 300 es
|
|
988
1089
|
#line 189
|
|
989
1090
|
precision highp int;
|
|
990
1091
|
precision highp float;
|
|
@@ -1013,10 +1114,13 @@ var fragColorbarShader = `#version 300 es
|
|
|
1013
1114
|
precision highp int;
|
|
1014
1115
|
precision highp float;
|
|
1015
1116
|
uniform highp sampler2D colormap;
|
|
1117
|
+
uniform float layer;
|
|
1016
1118
|
in vec2 vColor;
|
|
1017
1119
|
out vec4 color;
|
|
1018
1120
|
void main() {
|
|
1019
|
-
|
|
1121
|
+
float nlayer = float(textureSize(colormap, 0).y);
|
|
1122
|
+
float fmap = (0.5 + layer) / nlayer;
|
|
1123
|
+
color = vec4(texture(colormap, vec2(vColor.x, fmap)).rgb, 1.0);
|
|
1020
1124
|
}`;
|
|
1021
1125
|
var vertGraphShader = `#version 300 es
|
|
1022
1126
|
#line 229
|
|
@@ -1057,7 +1161,7 @@ void main(void) {
|
|
|
1057
1161
|
frac = (frac * 2.0) - 1.0;
|
|
1058
1162
|
gl_Position = vec4(frac, 0.0, 1.0);
|
|
1059
1163
|
}`;
|
|
1060
|
-
var
|
|
1164
|
+
var vertRectShader = `#version 300 es
|
|
1061
1165
|
#line 229
|
|
1062
1166
|
layout(location=0) in vec3 pos;
|
|
1063
1167
|
uniform vec2 canvasWidthHeight;
|
|
@@ -1070,6 +1174,20 @@ void main(void) {
|
|
|
1070
1174
|
frac = (frac * 2.0) - 1.0;
|
|
1071
1175
|
gl_Position = vec4(frac, 0.0, 1.0);
|
|
1072
1176
|
}`;
|
|
1177
|
+
var vertLineShader = `#version 300 es
|
|
1178
|
+
#line 229
|
|
1179
|
+
layout(location=0) in vec3 pos;
|
|
1180
|
+
uniform vec2 canvasWidthHeight;
|
|
1181
|
+
uniform float thickness;
|
|
1182
|
+
uniform vec4 startXYendXY;
|
|
1183
|
+
void main(void) {
|
|
1184
|
+
vec2 posXY = mix(startXYendXY.xy, startXYendXY.zw, pos.x);
|
|
1185
|
+
vec2 dir = normalize(startXYendXY.xy - startXYendXY.zw);
|
|
1186
|
+
posXY += vec2(-dir.y, dir.x) * thickness * (pos.y - 0.5);
|
|
1187
|
+
posXY.x = (posXY.x) / canvasWidthHeight.x; //0..1
|
|
1188
|
+
posXY.y = 1.0 - (posXY.y / canvasWidthHeight.y); //1..0
|
|
1189
|
+
gl_Position = vec4((posXY * 2.0) - 1.0, 0.0, 1.0);
|
|
1190
|
+
}`;
|
|
1073
1191
|
var vertBmpShader = `#version 300 es
|
|
1074
1192
|
#line 229
|
|
1075
1193
|
layout(location=0) in vec3 pos;
|
|
@@ -1156,7 +1274,7 @@ in vec2 TexCoord;
|
|
|
1156
1274
|
out vec4 FragColor;
|
|
1157
1275
|
uniform float coordZ;
|
|
1158
1276
|
uniform float layer;
|
|
1159
|
-
uniform float numLayers;
|
|
1277
|
+
//uniform float numLayers;
|
|
1160
1278
|
uniform highp sampler2D colormap;
|
|
1161
1279
|
uniform lowp sampler3D blend3D;
|
|
1162
1280
|
uniform float opacity;
|
|
@@ -1186,7 +1304,9 @@ void main(void) {
|
|
|
1186
1304
|
}
|
|
1187
1305
|
idx = ((idx - uint(1)) % uint(100))+uint(1);
|
|
1188
1306
|
float fx = (float(idx)+0.5) / 256.0;
|
|
1189
|
-
float
|
|
1307
|
+
float nlayer = float(textureSize(colormap, 0).y) * 0.5; //0.5 as both each layer has positive and negative color slot
|
|
1308
|
+
float y = (2.0 * layer + 1.0)/(4.0 * nlayer);
|
|
1309
|
+
//float y = (2.0 * layer + 1.0)/(4.0 * numLayers);
|
|
1190
1310
|
FragColor = texture(colormap, vec2(fx, y)).rgba;
|
|
1191
1311
|
FragColor.a *= opacity;
|
|
1192
1312
|
if (layer < 2.0) return;
|
|
@@ -1205,28 +1325,43 @@ in vec2 TexCoord;
|
|
|
1205
1325
|
out vec4 FragColor;
|
|
1206
1326
|
uniform float coordZ;
|
|
1207
1327
|
uniform float layer;
|
|
1208
|
-
uniform float numLayers;
|
|
1328
|
+
//uniform float numLayers;
|
|
1209
1329
|
uniform float scl_slope;
|
|
1210
1330
|
uniform float scl_inter;
|
|
1211
1331
|
uniform float cal_max;
|
|
1212
1332
|
uniform float cal_min;
|
|
1213
1333
|
uniform highp sampler2D colormap;
|
|
1214
1334
|
uniform lowp sampler3D blend3D;
|
|
1335
|
+
uniform int modulation;
|
|
1336
|
+
uniform highp sampler3D modulationVol;
|
|
1215
1337
|
uniform float opacity;
|
|
1216
1338
|
uniform mat4 mtx;
|
|
1217
1339
|
void main(void) {
|
|
1218
1340
|
vec4 vx = vec4(TexCoord.xy, coordZ, 1.0) * mtx;
|
|
1219
1341
|
float f = (scl_slope * float(texture(intensityVol, vx.xyz).r)) + scl_inter;
|
|
1342
|
+
bool isNegative = (f < 0.0);
|
|
1220
1343
|
float r = max(0.00001, abs(cal_max - cal_min));
|
|
1221
1344
|
float mn = min(cal_min, cal_max);
|
|
1222
|
-
|
|
1223
|
-
//float y = 1.0 / numLayers;
|
|
1224
|
-
//y = ((layer + 0.5) * y);
|
|
1345
|
+
float txl = mix(0.0, 1.0, (f - mn) / r);
|
|
1225
1346
|
//https://stackoverflow.com/questions/5879403/opengl-texture-coordinates-in-pixel-space
|
|
1226
|
-
float
|
|
1227
|
-
|
|
1347
|
+
float nlayer = float(textureSize(colormap, 0).y) * 0.5; //0.5 as both each layer has positive and negative color slot
|
|
1348
|
+
float y = (2.0 * layer + 1.0)/(4.0 * nlayer);
|
|
1349
|
+
FragColor = texture(colormap, vec2(txl, y)).rgba;
|
|
1350
|
+
if (isNegative) {
|
|
1351
|
+
y = (2.0 * layer + nlayer + nlayer + 1.0)/(4.0 * nlayer);
|
|
1352
|
+
//select texels at positions 0 and 1 of lookup table:
|
|
1353
|
+
vec4 v0 = texture(colormap, vec2(0.5/256.0, y));
|
|
1354
|
+
vec4 v1 = texture(colormap, vec2(1.5/256.0, y));
|
|
1355
|
+
txl = mix(0.0, 1.0, (- f - mn) / r);
|
|
1356
|
+
//detect bogus color: negative color slot not used than
|
|
1357
|
+
// v0 = 1,1,1,0 and v1 = 0,0,0,1
|
|
1358
|
+
if ((v0.r != 1.0) || (v0.a != 0.0) || (v1.r != 0.0) || (v1.a != 1.0))
|
|
1359
|
+
FragColor = texture(colormap, vec2(txl, y));
|
|
1360
|
+
}
|
|
1228
1361
|
if (layer > 0.7)
|
|
1229
1362
|
FragColor.a = step(0.00001, FragColor.a);
|
|
1363
|
+
if (modulation > 0)
|
|
1364
|
+
FragColor.rgb *= texture(modulationVol, vx.xyz).r;
|
|
1230
1365
|
FragColor.a *= opacity;
|
|
1231
1366
|
if (layer < 1.0) return;
|
|
1232
1367
|
vec2 texXY = TexCoord.xy*0.5 +vec2(0.5,0.5);
|
|
@@ -1244,7 +1379,7 @@ in vec2 TexCoord;
|
|
|
1244
1379
|
out vec4 FragColor;
|
|
1245
1380
|
uniform float coordZ;
|
|
1246
1381
|
uniform float layer;
|
|
1247
|
-
uniform float numLayers;
|
|
1382
|
+
//uniform float numLayers;
|
|
1248
1383
|
uniform float scl_slope;
|
|
1249
1384
|
uniform float scl_inter;
|
|
1250
1385
|
uniform float cal_max;
|
|
@@ -1254,10 +1389,14 @@ uniform lowp sampler3D blend3D;
|
|
|
1254
1389
|
uniform float opacity;
|
|
1255
1390
|
uniform mat4 mtx;
|
|
1256
1391
|
uniform bool hasAlpha;
|
|
1392
|
+
uniform int modulation;
|
|
1393
|
+
uniform highp sampler3D modulationVol;
|
|
1257
1394
|
void main(void) {
|
|
1258
1395
|
vec4 vx = vec4(TexCoord.xy, coordZ, 1.0) * mtx;
|
|
1259
1396
|
uvec4 aColor = texture(intensityVol, vx.xyz);
|
|
1260
1397
|
FragColor = vec4(float(aColor.r) / 255.0, float(aColor.g) / 255.0, float(aColor.b) / 255.0, float(aColor.a) / 255.0);
|
|
1398
|
+
if (modulation > 0)
|
|
1399
|
+
FragColor.rgb *= texture(modulationVol, vx.xyz).r;
|
|
1261
1400
|
if (!hasAlpha)
|
|
1262
1401
|
FragColor.a = (FragColor.r * 0.21 + FragColor.g * 0.72 + FragColor.b * 0.07);
|
|
1263
1402
|
FragColor.a *= opacity;
|
|
@@ -1404,6 +1543,21 @@ void main(void) {
|
|
|
1404
1543
|
vV = -vec3(modelMtx*vec4(pos,1.0));
|
|
1405
1544
|
vClr = clr;
|
|
1406
1545
|
}`;
|
|
1546
|
+
var fragMeshDepthShader = `#version 300 es
|
|
1547
|
+
precision highp int;
|
|
1548
|
+
precision highp float;
|
|
1549
|
+
uniform float opacity;
|
|
1550
|
+
out vec4 color;
|
|
1551
|
+
vec4 packFloatToVec4i(const float value) {
|
|
1552
|
+
const vec4 bitSh = vec4(256.0*256.0*256.0, 256.0*256.0, 256.0, 1.0);
|
|
1553
|
+
const vec4 bitMsk = vec4(0.0, 1.0/256.0, 1.0/256.0, 1.0/256.0);
|
|
1554
|
+
vec4 res = fract(value * bitSh);
|
|
1555
|
+
res -= res.xxyz * bitMsk;
|
|
1556
|
+
return res;
|
|
1557
|
+
}
|
|
1558
|
+
void main() {
|
|
1559
|
+
color = packFloatToVec4i(gl_FragCoord.z);
|
|
1560
|
+
}`;
|
|
1407
1561
|
var fragMeshToonShader = `#version 300 es
|
|
1408
1562
|
precision highp int;
|
|
1409
1563
|
precision highp float;
|
|
@@ -1599,15 +1753,6 @@ void main() {
|
|
|
1599
1753
|
vec3 d = vClr.rgb * diffuse * SH(-reflect(n, vec3(l.x, l.y, -l.z)) );
|
|
1600
1754
|
color = vec4(a + d + s, opacity);
|
|
1601
1755
|
}`;
|
|
1602
|
-
var fragDepthPickingShader = `#version 300 es
|
|
1603
|
-
precision highp int;
|
|
1604
|
-
precision highp float;
|
|
1605
|
-
uniform int id;
|
|
1606
|
-
in vec3 vColor;
|
|
1607
|
-
out vec4 color;
|
|
1608
|
-
void main() {
|
|
1609
|
-
color = vec4(vColor, float(id & 255) / 255.0);
|
|
1610
|
-
}`;
|
|
1611
1756
|
var fragVolumePickingShader = `#version 300 es
|
|
1612
1757
|
#line 506
|
|
1613
1758
|
//precision highp int;
|
|
@@ -1618,12 +1763,15 @@ uniform vec3 texVox;
|
|
|
1618
1763
|
uniform vec4 clipPlane;
|
|
1619
1764
|
uniform highp sampler3D volume, overlay;
|
|
1620
1765
|
uniform float overlays;
|
|
1621
|
-
uniform
|
|
1766
|
+
uniform mat4 matRAS;
|
|
1767
|
+
uniform mat4 mvpMtx;
|
|
1622
1768
|
in vec3 vColor;
|
|
1623
1769
|
out vec4 fColor;
|
|
1624
1770
|
` + kRenderFunc + `
|
|
1625
1771
|
void main() {
|
|
1772
|
+
int id = 254;
|
|
1626
1773
|
vec3 start = vColor;
|
|
1774
|
+
gl_FragDepth = 0.0;
|
|
1627
1775
|
fColor = vec4(0.0, 0.0, 0.0, 0.0); //assume no hit: ID = 0
|
|
1628
1776
|
float fid = float(id & 255)/ 255.0;
|
|
1629
1777
|
vec3 backPosition = GetBackPosition(start);
|
|
@@ -1647,6 +1795,7 @@ void main() {
|
|
|
1647
1795
|
float val = texture(volume, samplePos.xyz).a;
|
|
1648
1796
|
if (val > 0.01) {
|
|
1649
1797
|
fColor = vec4(samplePos.rgb, fid);
|
|
1798
|
+
gl_FragDepth = frac2ndc(samplePos.xyz);
|
|
1650
1799
|
break;
|
|
1651
1800
|
}
|
|
1652
1801
|
samplePos += deltaDirFast; //advance ray position
|
|
@@ -1663,6 +1812,7 @@ void main() {
|
|
|
1663
1812
|
float val = texture(overlay, samplePos.xyz).a;
|
|
1664
1813
|
if (val > 0.01) {
|
|
1665
1814
|
fColor = vec4(samplePos.rgb, fid);
|
|
1815
|
+
gl_FragDepth = frac2ndc(samplePos.xyz);
|
|
1666
1816
|
return;
|
|
1667
1817
|
}
|
|
1668
1818
|
samplePos += deltaDirFast; //advance ray position
|
|
@@ -1670,6 +1820,34 @@ void main() {
|
|
|
1670
1820
|
//if (fColor.a == 0.0) discard; //no hit in either background or overlays
|
|
1671
1821
|
//you only get here if there is a hit with the background that is closer than any overlay
|
|
1672
1822
|
}`;
|
|
1823
|
+
var vertOrientCubeShader = `#version 300 es
|
|
1824
|
+
// an attribute is an input (in) to a vertex shader.
|
|
1825
|
+
// It will receive data from a buffer
|
|
1826
|
+
layout(location=0) in vec3 a_position;
|
|
1827
|
+
layout(location=1) in vec3 a_color;
|
|
1828
|
+
// A matrix to transform the positions by
|
|
1829
|
+
uniform mat4 u_matrix;
|
|
1830
|
+
out vec3 vColor;
|
|
1831
|
+
// all shaders have a main function
|
|
1832
|
+
void main() {
|
|
1833
|
+
// Multiply the position by the matrix.
|
|
1834
|
+
vec4 pos = vec4(a_position, 1.0);
|
|
1835
|
+
gl_Position = u_matrix * vec4(pos);
|
|
1836
|
+
vColor = a_color;
|
|
1837
|
+
}
|
|
1838
|
+
`;
|
|
1839
|
+
var fragOrientCubeShader = `#version 300 es
|
|
1840
|
+
precision highp float;
|
|
1841
|
+
|
|
1842
|
+
uniform vec4 u_color;
|
|
1843
|
+
in vec3 vColor;
|
|
1844
|
+
// we need to declare an output for the fragment shader
|
|
1845
|
+
out vec4 outColor;
|
|
1846
|
+
|
|
1847
|
+
void main() {
|
|
1848
|
+
outColor = vec4(vColor, 1.0);
|
|
1849
|
+
}
|
|
1850
|
+
`;
|
|
1673
1851
|
/*! *****************************************************************************
|
|
1674
1852
|
Copyright (c) Microsoft Corporation.
|
|
1675
1853
|
|
|
@@ -2709,6 +2887,1016 @@ function interval(period, scheduler) {
|
|
|
2709
2887
|
}
|
|
2710
2888
|
return timer(period, period, scheduler);
|
|
2711
2889
|
}
|
|
2890
|
+
const orientCube = new Float32Array([
|
|
2891
|
+
-1,
|
|
2892
|
+
-1,
|
|
2893
|
+
-1,
|
|
2894
|
+
0.28,
|
|
2895
|
+
0.28,
|
|
2896
|
+
0.28,
|
|
2897
|
+
-1,
|
|
2898
|
+
-1,
|
|
2899
|
+
-1,
|
|
2900
|
+
0.28,
|
|
2901
|
+
0.28,
|
|
2902
|
+
0.28,
|
|
2903
|
+
-1,
|
|
2904
|
+
1,
|
|
2905
|
+
-1,
|
|
2906
|
+
0.28,
|
|
2907
|
+
0.28,
|
|
2908
|
+
0.28,
|
|
2909
|
+
1,
|
|
2910
|
+
-1,
|
|
2911
|
+
-1,
|
|
2912
|
+
0.28,
|
|
2913
|
+
0.28,
|
|
2914
|
+
0.28,
|
|
2915
|
+
1,
|
|
2916
|
+
1,
|
|
2917
|
+
-1,
|
|
2918
|
+
0.28,
|
|
2919
|
+
0.28,
|
|
2920
|
+
0.28,
|
|
2921
|
+
1,
|
|
2922
|
+
1,
|
|
2923
|
+
-1,
|
|
2924
|
+
0.28,
|
|
2925
|
+
0.28,
|
|
2926
|
+
0.28,
|
|
2927
|
+
-1,
|
|
2928
|
+
-1,
|
|
2929
|
+
1,
|
|
2930
|
+
0.8,
|
|
2931
|
+
0.8,
|
|
2932
|
+
0.8,
|
|
2933
|
+
-1,
|
|
2934
|
+
-1,
|
|
2935
|
+
1,
|
|
2936
|
+
0.8,
|
|
2937
|
+
0.8,
|
|
2938
|
+
0.8,
|
|
2939
|
+
1,
|
|
2940
|
+
-1,
|
|
2941
|
+
1,
|
|
2942
|
+
0.8,
|
|
2943
|
+
0.8,
|
|
2944
|
+
0.8,
|
|
2945
|
+
-1,
|
|
2946
|
+
1,
|
|
2947
|
+
1,
|
|
2948
|
+
0.8,
|
|
2949
|
+
0.8,
|
|
2950
|
+
0.8,
|
|
2951
|
+
1,
|
|
2952
|
+
1,
|
|
2953
|
+
1,
|
|
2954
|
+
0.8,
|
|
2955
|
+
0.8,
|
|
2956
|
+
0.8,
|
|
2957
|
+
1,
|
|
2958
|
+
1,
|
|
2959
|
+
1,
|
|
2960
|
+
0.8,
|
|
2961
|
+
0.8,
|
|
2962
|
+
0.8,
|
|
2963
|
+
-1,
|
|
2964
|
+
1,
|
|
2965
|
+
-1,
|
|
2966
|
+
0,
|
|
2967
|
+
0,
|
|
2968
|
+
0.74,
|
|
2969
|
+
-1,
|
|
2970
|
+
1,
|
|
2971
|
+
-1,
|
|
2972
|
+
0,
|
|
2973
|
+
0,
|
|
2974
|
+
0.74,
|
|
2975
|
+
-1,
|
|
2976
|
+
1,
|
|
2977
|
+
1,
|
|
2978
|
+
0,
|
|
2979
|
+
0,
|
|
2980
|
+
0.74,
|
|
2981
|
+
1,
|
|
2982
|
+
1,
|
|
2983
|
+
-1,
|
|
2984
|
+
0,
|
|
2985
|
+
0,
|
|
2986
|
+
0.74,
|
|
2987
|
+
1,
|
|
2988
|
+
1,
|
|
2989
|
+
1,
|
|
2990
|
+
0,
|
|
2991
|
+
0,
|
|
2992
|
+
0.74,
|
|
2993
|
+
1,
|
|
2994
|
+
1,
|
|
2995
|
+
1,
|
|
2996
|
+
0,
|
|
2997
|
+
0,
|
|
2998
|
+
0.74,
|
|
2999
|
+
-1,
|
|
3000
|
+
-1,
|
|
3001
|
+
-1,
|
|
3002
|
+
0.42,
|
|
3003
|
+
0,
|
|
3004
|
+
0.42,
|
|
3005
|
+
-1,
|
|
3006
|
+
-1,
|
|
3007
|
+
-1,
|
|
3008
|
+
0.42,
|
|
3009
|
+
0,
|
|
3010
|
+
0.42,
|
|
3011
|
+
1,
|
|
3012
|
+
-1,
|
|
3013
|
+
-1,
|
|
3014
|
+
0.42,
|
|
3015
|
+
0,
|
|
3016
|
+
0.42,
|
|
3017
|
+
-1,
|
|
3018
|
+
-1,
|
|
3019
|
+
1,
|
|
3020
|
+
0.42,
|
|
3021
|
+
0,
|
|
3022
|
+
0.42,
|
|
3023
|
+
1,
|
|
3024
|
+
-1,
|
|
3025
|
+
1,
|
|
3026
|
+
0.42,
|
|
3027
|
+
0,
|
|
3028
|
+
0.42,
|
|
3029
|
+
1,
|
|
3030
|
+
-1,
|
|
3031
|
+
1,
|
|
3032
|
+
0.42,
|
|
3033
|
+
0,
|
|
3034
|
+
0.42,
|
|
3035
|
+
-1,
|
|
3036
|
+
-1,
|
|
3037
|
+
-1,
|
|
3038
|
+
0.64,
|
|
3039
|
+
0,
|
|
3040
|
+
0,
|
|
3041
|
+
-1,
|
|
3042
|
+
-1,
|
|
3043
|
+
-1,
|
|
3044
|
+
0.64,
|
|
3045
|
+
0,
|
|
3046
|
+
0,
|
|
3047
|
+
-1,
|
|
3048
|
+
-1,
|
|
3049
|
+
1,
|
|
3050
|
+
0.64,
|
|
3051
|
+
0,
|
|
3052
|
+
0,
|
|
3053
|
+
-1,
|
|
3054
|
+
1,
|
|
3055
|
+
-1,
|
|
3056
|
+
0.64,
|
|
3057
|
+
0,
|
|
3058
|
+
0,
|
|
3059
|
+
-1,
|
|
3060
|
+
1,
|
|
3061
|
+
1,
|
|
3062
|
+
0.64,
|
|
3063
|
+
0,
|
|
3064
|
+
0,
|
|
3065
|
+
-1,
|
|
3066
|
+
1,
|
|
3067
|
+
1,
|
|
3068
|
+
0.64,
|
|
3069
|
+
0,
|
|
3070
|
+
0,
|
|
3071
|
+
1,
|
|
3072
|
+
-1,
|
|
3073
|
+
-1,
|
|
3074
|
+
0,
|
|
3075
|
+
0.5,
|
|
3076
|
+
0,
|
|
3077
|
+
1,
|
|
3078
|
+
-1,
|
|
3079
|
+
-1,
|
|
3080
|
+
0,
|
|
3081
|
+
0.5,
|
|
3082
|
+
0,
|
|
3083
|
+
1,
|
|
3084
|
+
1,
|
|
3085
|
+
-1,
|
|
3086
|
+
0,
|
|
3087
|
+
0.5,
|
|
3088
|
+
0,
|
|
3089
|
+
1,
|
|
3090
|
+
-1,
|
|
3091
|
+
1,
|
|
3092
|
+
0,
|
|
3093
|
+
0.5,
|
|
3094
|
+
0,
|
|
3095
|
+
1,
|
|
3096
|
+
1,
|
|
3097
|
+
1,
|
|
3098
|
+
0,
|
|
3099
|
+
0.5,
|
|
3100
|
+
0,
|
|
3101
|
+
1,
|
|
3102
|
+
1,
|
|
3103
|
+
1,
|
|
3104
|
+
0,
|
|
3105
|
+
0.5,
|
|
3106
|
+
0,
|
|
3107
|
+
-0.45,
|
|
3108
|
+
1,
|
|
3109
|
+
-0.8,
|
|
3110
|
+
0,
|
|
3111
|
+
0,
|
|
3112
|
+
0,
|
|
3113
|
+
-0.45,
|
|
3114
|
+
1,
|
|
3115
|
+
-0.8,
|
|
3116
|
+
0,
|
|
3117
|
+
0,
|
|
3118
|
+
0,
|
|
3119
|
+
-0.45,
|
|
3120
|
+
1,
|
|
3121
|
+
0.8,
|
|
3122
|
+
0,
|
|
3123
|
+
0,
|
|
3124
|
+
0,
|
|
3125
|
+
-0.25,
|
|
3126
|
+
1,
|
|
3127
|
+
-0.8,
|
|
3128
|
+
0,
|
|
3129
|
+
0,
|
|
3130
|
+
0,
|
|
3131
|
+
-0.25,
|
|
3132
|
+
1,
|
|
3133
|
+
0.8,
|
|
3134
|
+
0,
|
|
3135
|
+
0,
|
|
3136
|
+
0,
|
|
3137
|
+
-0.25,
|
|
3138
|
+
1,
|
|
3139
|
+
0.8,
|
|
3140
|
+
0,
|
|
3141
|
+
0,
|
|
3142
|
+
0,
|
|
3143
|
+
-0.25,
|
|
3144
|
+
1,
|
|
3145
|
+
0.6,
|
|
3146
|
+
0,
|
|
3147
|
+
0,
|
|
3148
|
+
0,
|
|
3149
|
+
-0.25,
|
|
3150
|
+
1,
|
|
3151
|
+
0.6,
|
|
3152
|
+
0,
|
|
3153
|
+
0,
|
|
3154
|
+
0,
|
|
3155
|
+
-0.25,
|
|
3156
|
+
1,
|
|
3157
|
+
0.8,
|
|
3158
|
+
0,
|
|
3159
|
+
0,
|
|
3160
|
+
0,
|
|
3161
|
+
0.45,
|
|
3162
|
+
1,
|
|
3163
|
+
0.6,
|
|
3164
|
+
0,
|
|
3165
|
+
0,
|
|
3166
|
+
0,
|
|
3167
|
+
0.25,
|
|
3168
|
+
1,
|
|
3169
|
+
0.8,
|
|
3170
|
+
0,
|
|
3171
|
+
0,
|
|
3172
|
+
0,
|
|
3173
|
+
0.25,
|
|
3174
|
+
1,
|
|
3175
|
+
0.8,
|
|
3176
|
+
0,
|
|
3177
|
+
0,
|
|
3178
|
+
0,
|
|
3179
|
+
0.25,
|
|
3180
|
+
1,
|
|
3181
|
+
0.1,
|
|
3182
|
+
0,
|
|
3183
|
+
0,
|
|
3184
|
+
0,
|
|
3185
|
+
0.25,
|
|
3186
|
+
1,
|
|
3187
|
+
0.1,
|
|
3188
|
+
0,
|
|
3189
|
+
0,
|
|
3190
|
+
0,
|
|
3191
|
+
0.25,
|
|
3192
|
+
1,
|
|
3193
|
+
0.6,
|
|
3194
|
+
0,
|
|
3195
|
+
0,
|
|
3196
|
+
0,
|
|
3197
|
+
0.45,
|
|
3198
|
+
1,
|
|
3199
|
+
0.1,
|
|
3200
|
+
0,
|
|
3201
|
+
0,
|
|
3202
|
+
0,
|
|
3203
|
+
0.45,
|
|
3204
|
+
1,
|
|
3205
|
+
0.6,
|
|
3206
|
+
0,
|
|
3207
|
+
0,
|
|
3208
|
+
0,
|
|
3209
|
+
0.45,
|
|
3210
|
+
1,
|
|
3211
|
+
0.6,
|
|
3212
|
+
0,
|
|
3213
|
+
0,
|
|
3214
|
+
0,
|
|
3215
|
+
-0.25,
|
|
3216
|
+
1,
|
|
3217
|
+
-0.1,
|
|
3218
|
+
0,
|
|
3219
|
+
0,
|
|
3220
|
+
0,
|
|
3221
|
+
-0.25,
|
|
3222
|
+
1,
|
|
3223
|
+
-0.1,
|
|
3224
|
+
0,
|
|
3225
|
+
0,
|
|
3226
|
+
0,
|
|
3227
|
+
-0.25,
|
|
3228
|
+
1,
|
|
3229
|
+
0.1,
|
|
3230
|
+
0,
|
|
3231
|
+
0,
|
|
3232
|
+
0,
|
|
3233
|
+
0.25,
|
|
3234
|
+
1,
|
|
3235
|
+
-0.1,
|
|
3236
|
+
0,
|
|
3237
|
+
0,
|
|
3238
|
+
0,
|
|
3239
|
+
0.45,
|
|
3240
|
+
1,
|
|
3241
|
+
0.1,
|
|
3242
|
+
0,
|
|
3243
|
+
0,
|
|
3244
|
+
0,
|
|
3245
|
+
0.45,
|
|
3246
|
+
1,
|
|
3247
|
+
0.1,
|
|
3248
|
+
0,
|
|
3249
|
+
0,
|
|
3250
|
+
0,
|
|
3251
|
+
0.45,
|
|
3252
|
+
-1,
|
|
3253
|
+
-0.8,
|
|
3254
|
+
0,
|
|
3255
|
+
0,
|
|
3256
|
+
0,
|
|
3257
|
+
0.45,
|
|
3258
|
+
-1,
|
|
3259
|
+
-0.8,
|
|
3260
|
+
0,
|
|
3261
|
+
0,
|
|
3262
|
+
0,
|
|
3263
|
+
0.05,
|
|
3264
|
+
-1,
|
|
3265
|
+
0.8,
|
|
3266
|
+
0,
|
|
3267
|
+
0,
|
|
3268
|
+
0,
|
|
3269
|
+
0.25,
|
|
3270
|
+
-1,
|
|
3271
|
+
-0.8,
|
|
3272
|
+
0,
|
|
3273
|
+
0,
|
|
3274
|
+
0,
|
|
3275
|
+
-0.15,
|
|
3276
|
+
-1,
|
|
3277
|
+
0.8,
|
|
3278
|
+
0,
|
|
3279
|
+
0,
|
|
3280
|
+
0,
|
|
3281
|
+
-0.15,
|
|
3282
|
+
-1,
|
|
3283
|
+
0.8,
|
|
3284
|
+
0,
|
|
3285
|
+
0,
|
|
3286
|
+
0,
|
|
3287
|
+
-0.25,
|
|
3288
|
+
-1,
|
|
3289
|
+
-0.8,
|
|
3290
|
+
0,
|
|
3291
|
+
0,
|
|
3292
|
+
0,
|
|
3293
|
+
-0.25,
|
|
3294
|
+
-1,
|
|
3295
|
+
-0.8,
|
|
3296
|
+
0,
|
|
3297
|
+
0,
|
|
3298
|
+
0,
|
|
3299
|
+
0.05,
|
|
3300
|
+
-1,
|
|
3301
|
+
0.8,
|
|
3302
|
+
0,
|
|
3303
|
+
0,
|
|
3304
|
+
0,
|
|
3305
|
+
-0.45,
|
|
3306
|
+
-1,
|
|
3307
|
+
-0.8,
|
|
3308
|
+
0,
|
|
3309
|
+
0,
|
|
3310
|
+
0,
|
|
3311
|
+
-0.15,
|
|
3312
|
+
-1,
|
|
3313
|
+
0.8,
|
|
3314
|
+
0,
|
|
3315
|
+
0,
|
|
3316
|
+
0,
|
|
3317
|
+
-0.15,
|
|
3318
|
+
-1,
|
|
3319
|
+
0.8,
|
|
3320
|
+
0,
|
|
3321
|
+
0,
|
|
3322
|
+
0,
|
|
3323
|
+
0.13,
|
|
3324
|
+
-1,
|
|
3325
|
+
-0.3,
|
|
3326
|
+
0,
|
|
3327
|
+
0,
|
|
3328
|
+
0,
|
|
3329
|
+
0.13,
|
|
3330
|
+
-1,
|
|
3331
|
+
-0.3,
|
|
3332
|
+
0,
|
|
3333
|
+
0,
|
|
3334
|
+
0,
|
|
3335
|
+
0.07,
|
|
3336
|
+
-1,
|
|
3337
|
+
-0.1,
|
|
3338
|
+
0,
|
|
3339
|
+
0,
|
|
3340
|
+
0,
|
|
3341
|
+
-0.33,
|
|
3342
|
+
-1,
|
|
3343
|
+
-0.3,
|
|
3344
|
+
0,
|
|
3345
|
+
0,
|
|
3346
|
+
0,
|
|
3347
|
+
-0.27,
|
|
3348
|
+
-1,
|
|
3349
|
+
-0.1,
|
|
3350
|
+
0,
|
|
3351
|
+
0,
|
|
3352
|
+
0,
|
|
3353
|
+
-0.27,
|
|
3354
|
+
-1,
|
|
3355
|
+
-0.1,
|
|
3356
|
+
0,
|
|
3357
|
+
0,
|
|
3358
|
+
0,
|
|
3359
|
+
-0.45,
|
|
3360
|
+
0.6,
|
|
3361
|
+
1,
|
|
3362
|
+
0,
|
|
3363
|
+
0,
|
|
3364
|
+
0,
|
|
3365
|
+
-0.45,
|
|
3366
|
+
0.6,
|
|
3367
|
+
1,
|
|
3368
|
+
0,
|
|
3369
|
+
0,
|
|
3370
|
+
0,
|
|
3371
|
+
-0.45,
|
|
3372
|
+
0.4,
|
|
3373
|
+
1,
|
|
3374
|
+
0,
|
|
3375
|
+
0,
|
|
3376
|
+
0,
|
|
3377
|
+
-0.25,
|
|
3378
|
+
0.8,
|
|
3379
|
+
1,
|
|
3380
|
+
0,
|
|
3381
|
+
0,
|
|
3382
|
+
0,
|
|
3383
|
+
-0.25,
|
|
3384
|
+
0.4,
|
|
3385
|
+
1,
|
|
3386
|
+
0,
|
|
3387
|
+
0,
|
|
3388
|
+
0,
|
|
3389
|
+
-0.25,
|
|
3390
|
+
0.4,
|
|
3391
|
+
1,
|
|
3392
|
+
0,
|
|
3393
|
+
0,
|
|
3394
|
+
0,
|
|
3395
|
+
-0.25,
|
|
3396
|
+
0.8,
|
|
3397
|
+
1,
|
|
3398
|
+
0,
|
|
3399
|
+
0,
|
|
3400
|
+
0,
|
|
3401
|
+
-0.25,
|
|
3402
|
+
0.8,
|
|
3403
|
+
1,
|
|
3404
|
+
0,
|
|
3405
|
+
0,
|
|
3406
|
+
0,
|
|
3407
|
+
-0.25,
|
|
3408
|
+
0.6,
|
|
3409
|
+
1,
|
|
3410
|
+
0,
|
|
3411
|
+
0,
|
|
3412
|
+
0,
|
|
3413
|
+
0.25,
|
|
3414
|
+
0.8,
|
|
3415
|
+
1,
|
|
3416
|
+
0,
|
|
3417
|
+
0,
|
|
3418
|
+
0,
|
|
3419
|
+
0.45,
|
|
3420
|
+
0.6,
|
|
3421
|
+
1,
|
|
3422
|
+
0,
|
|
3423
|
+
0,
|
|
3424
|
+
0,
|
|
3425
|
+
0.45,
|
|
3426
|
+
0.6,
|
|
3427
|
+
1,
|
|
3428
|
+
0,
|
|
3429
|
+
0,
|
|
3430
|
+
0,
|
|
3431
|
+
0.25,
|
|
3432
|
+
0.8,
|
|
3433
|
+
1,
|
|
3434
|
+
0,
|
|
3435
|
+
0,
|
|
3436
|
+
0,
|
|
3437
|
+
0.25,
|
|
3438
|
+
0.8,
|
|
3439
|
+
1,
|
|
3440
|
+
0,
|
|
3441
|
+
0,
|
|
3442
|
+
0,
|
|
3443
|
+
0.25,
|
|
3444
|
+
-0.1,
|
|
3445
|
+
1,
|
|
3446
|
+
0,
|
|
3447
|
+
0,
|
|
3448
|
+
0,
|
|
3449
|
+
0.45,
|
|
3450
|
+
0.6,
|
|
3451
|
+
1,
|
|
3452
|
+
0,
|
|
3453
|
+
0,
|
|
3454
|
+
0,
|
|
3455
|
+
0.45,
|
|
3456
|
+
0.1,
|
|
3457
|
+
1,
|
|
3458
|
+
0,
|
|
3459
|
+
0,
|
|
3460
|
+
0,
|
|
3461
|
+
0.45,
|
|
3462
|
+
0.1,
|
|
3463
|
+
1,
|
|
3464
|
+
0,
|
|
3465
|
+
0,
|
|
3466
|
+
0,
|
|
3467
|
+
-0.25,
|
|
3468
|
+
0.1,
|
|
3469
|
+
1,
|
|
3470
|
+
0,
|
|
3471
|
+
0,
|
|
3472
|
+
0,
|
|
3473
|
+
-0.25,
|
|
3474
|
+
0.1,
|
|
3475
|
+
1,
|
|
3476
|
+
0,
|
|
3477
|
+
0,
|
|
3478
|
+
0,
|
|
3479
|
+
-0.45,
|
|
3480
|
+
-0.1,
|
|
3481
|
+
1,
|
|
3482
|
+
0,
|
|
3483
|
+
0,
|
|
3484
|
+
0,
|
|
3485
|
+
0.25,
|
|
3486
|
+
0.1,
|
|
3487
|
+
1,
|
|
3488
|
+
0,
|
|
3489
|
+
0,
|
|
3490
|
+
0,
|
|
3491
|
+
0.25,
|
|
3492
|
+
-0.1,
|
|
3493
|
+
1,
|
|
3494
|
+
0,
|
|
3495
|
+
0,
|
|
3496
|
+
0,
|
|
3497
|
+
0.25,
|
|
3498
|
+
-0.1,
|
|
3499
|
+
1,
|
|
3500
|
+
0,
|
|
3501
|
+
0,
|
|
3502
|
+
0,
|
|
3503
|
+
-0.45,
|
|
3504
|
+
-0.1,
|
|
3505
|
+
1,
|
|
3506
|
+
0,
|
|
3507
|
+
0,
|
|
3508
|
+
0,
|
|
3509
|
+
-0.45,
|
|
3510
|
+
-0.1,
|
|
3511
|
+
1,
|
|
3512
|
+
0,
|
|
3513
|
+
0,
|
|
3514
|
+
0,
|
|
3515
|
+
-0.45,
|
|
3516
|
+
-0.6,
|
|
3517
|
+
1,
|
|
3518
|
+
0,
|
|
3519
|
+
0,
|
|
3520
|
+
0,
|
|
3521
|
+
-0.25,
|
|
3522
|
+
-0.1,
|
|
3523
|
+
1,
|
|
3524
|
+
0,
|
|
3525
|
+
0,
|
|
3526
|
+
0,
|
|
3527
|
+
-0.25,
|
|
3528
|
+
-0.8,
|
|
3529
|
+
1,
|
|
3530
|
+
0,
|
|
3531
|
+
0,
|
|
3532
|
+
0,
|
|
3533
|
+
-0.25,
|
|
3534
|
+
-0.8,
|
|
3535
|
+
1,
|
|
3536
|
+
0,
|
|
3537
|
+
0,
|
|
3538
|
+
0,
|
|
3539
|
+
-0.25,
|
|
3540
|
+
-0.6,
|
|
3541
|
+
1,
|
|
3542
|
+
0,
|
|
3543
|
+
0,
|
|
3544
|
+
0,
|
|
3545
|
+
-0.25,
|
|
3546
|
+
-0.6,
|
|
3547
|
+
1,
|
|
3548
|
+
0,
|
|
3549
|
+
0,
|
|
3550
|
+
0,
|
|
3551
|
+
-0.25,
|
|
3552
|
+
-0.8,
|
|
3553
|
+
1,
|
|
3554
|
+
0,
|
|
3555
|
+
0,
|
|
3556
|
+
0,
|
|
3557
|
+
0.45,
|
|
3558
|
+
-0.6,
|
|
3559
|
+
1,
|
|
3560
|
+
0,
|
|
3561
|
+
0,
|
|
3562
|
+
0,
|
|
3563
|
+
0.25,
|
|
3564
|
+
-0.8,
|
|
3565
|
+
1,
|
|
3566
|
+
0,
|
|
3567
|
+
0,
|
|
3568
|
+
0,
|
|
3569
|
+
0.25,
|
|
3570
|
+
-0.8,
|
|
3571
|
+
1,
|
|
3572
|
+
0,
|
|
3573
|
+
0,
|
|
3574
|
+
0,
|
|
3575
|
+
0.25,
|
|
3576
|
+
-0.4,
|
|
3577
|
+
1,
|
|
3578
|
+
0,
|
|
3579
|
+
0,
|
|
3580
|
+
0,
|
|
3581
|
+
0.25,
|
|
3582
|
+
-0.4,
|
|
3583
|
+
1,
|
|
3584
|
+
0,
|
|
3585
|
+
0,
|
|
3586
|
+
0,
|
|
3587
|
+
0.25,
|
|
3588
|
+
-0.6,
|
|
3589
|
+
1,
|
|
3590
|
+
0,
|
|
3591
|
+
0,
|
|
3592
|
+
0,
|
|
3593
|
+
0.45,
|
|
3594
|
+
-0.4,
|
|
3595
|
+
1,
|
|
3596
|
+
0,
|
|
3597
|
+
0,
|
|
3598
|
+
0,
|
|
3599
|
+
0.45,
|
|
3600
|
+
-0.6,
|
|
3601
|
+
1,
|
|
3602
|
+
0,
|
|
3603
|
+
0,
|
|
3604
|
+
0,
|
|
3605
|
+
0.45,
|
|
3606
|
+
-0.6,
|
|
3607
|
+
1,
|
|
3608
|
+
0,
|
|
3609
|
+
0,
|
|
3610
|
+
0,
|
|
3611
|
+
-0.1,
|
|
3612
|
+
-0.8,
|
|
3613
|
+
-1,
|
|
3614
|
+
0,
|
|
3615
|
+
0,
|
|
3616
|
+
0,
|
|
3617
|
+
-0.1,
|
|
3618
|
+
-0.8,
|
|
3619
|
+
-1,
|
|
3620
|
+
0,
|
|
3621
|
+
0,
|
|
3622
|
+
0,
|
|
3623
|
+
-0.1,
|
|
3624
|
+
0.8,
|
|
3625
|
+
-1,
|
|
3626
|
+
0,
|
|
3627
|
+
0,
|
|
3628
|
+
0,
|
|
3629
|
+
0.1,
|
|
3630
|
+
-0.8,
|
|
3631
|
+
-1,
|
|
3632
|
+
0,
|
|
3633
|
+
0,
|
|
3634
|
+
0,
|
|
3635
|
+
0.1,
|
|
3636
|
+
0.8,
|
|
3637
|
+
-1,
|
|
3638
|
+
0,
|
|
3639
|
+
0,
|
|
3640
|
+
0,
|
|
3641
|
+
0.1,
|
|
3642
|
+
0.8,
|
|
3643
|
+
-1,
|
|
3644
|
+
0,
|
|
3645
|
+
0,
|
|
3646
|
+
0,
|
|
3647
|
+
-1,
|
|
3648
|
+
-0.45,
|
|
3649
|
+
-0.8,
|
|
3650
|
+
0,
|
|
3651
|
+
0,
|
|
3652
|
+
0,
|
|
3653
|
+
-1,
|
|
3654
|
+
-0.45,
|
|
3655
|
+
-0.8,
|
|
3656
|
+
0,
|
|
3657
|
+
0,
|
|
3658
|
+
0,
|
|
3659
|
+
-1,
|
|
3660
|
+
-0.45,
|
|
3661
|
+
0.8,
|
|
3662
|
+
0,
|
|
3663
|
+
0,
|
|
3664
|
+
0,
|
|
3665
|
+
-1,
|
|
3666
|
+
-0.25,
|
|
3667
|
+
-0.8,
|
|
3668
|
+
0,
|
|
3669
|
+
0,
|
|
3670
|
+
0,
|
|
3671
|
+
-1,
|
|
3672
|
+
-0.25,
|
|
3673
|
+
0.8,
|
|
3674
|
+
0,
|
|
3675
|
+
0,
|
|
3676
|
+
0,
|
|
3677
|
+
-1,
|
|
3678
|
+
-0.25,
|
|
3679
|
+
0.8,
|
|
3680
|
+
0,
|
|
3681
|
+
0,
|
|
3682
|
+
0,
|
|
3683
|
+
-1,
|
|
3684
|
+
-0.25,
|
|
3685
|
+
-0.8,
|
|
3686
|
+
0,
|
|
3687
|
+
0,
|
|
3688
|
+
0,
|
|
3689
|
+
-1,
|
|
3690
|
+
-0.25,
|
|
3691
|
+
-0.8,
|
|
3692
|
+
0,
|
|
3693
|
+
0,
|
|
3694
|
+
0,
|
|
3695
|
+
-1,
|
|
3696
|
+
-0.25,
|
|
3697
|
+
-0.6,
|
|
3698
|
+
0,
|
|
3699
|
+
0,
|
|
3700
|
+
0,
|
|
3701
|
+
-1,
|
|
3702
|
+
0.45,
|
|
3703
|
+
-0.8,
|
|
3704
|
+
0,
|
|
3705
|
+
0,
|
|
3706
|
+
0,
|
|
3707
|
+
-1,
|
|
3708
|
+
0.45,
|
|
3709
|
+
-0.6,
|
|
3710
|
+
0,
|
|
3711
|
+
0,
|
|
3712
|
+
0,
|
|
3713
|
+
-1,
|
|
3714
|
+
0.45,
|
|
3715
|
+
-0.6,
|
|
3716
|
+
0,
|
|
3717
|
+
0,
|
|
3718
|
+
0,
|
|
3719
|
+
1,
|
|
3720
|
+
0.45,
|
|
3721
|
+
-0.8,
|
|
3722
|
+
0,
|
|
3723
|
+
0,
|
|
3724
|
+
0,
|
|
3725
|
+
1,
|
|
3726
|
+
0.45,
|
|
3727
|
+
-0.8,
|
|
3728
|
+
0,
|
|
3729
|
+
0,
|
|
3730
|
+
0,
|
|
3731
|
+
1,
|
|
3732
|
+
0.45,
|
|
3733
|
+
0.8,
|
|
3734
|
+
0,
|
|
3735
|
+
0,
|
|
3736
|
+
0,
|
|
3737
|
+
1,
|
|
3738
|
+
0.25,
|
|
3739
|
+
-0.8,
|
|
3740
|
+
0,
|
|
3741
|
+
0,
|
|
3742
|
+
0,
|
|
3743
|
+
1,
|
|
3744
|
+
0.25,
|
|
3745
|
+
0.8,
|
|
3746
|
+
0,
|
|
3747
|
+
0,
|
|
3748
|
+
0,
|
|
3749
|
+
1,
|
|
3750
|
+
0.25,
|
|
3751
|
+
0.8,
|
|
3752
|
+
0,
|
|
3753
|
+
0,
|
|
3754
|
+
0,
|
|
3755
|
+
1,
|
|
3756
|
+
0.25,
|
|
3757
|
+
0.6,
|
|
3758
|
+
0,
|
|
3759
|
+
0,
|
|
3760
|
+
0,
|
|
3761
|
+
1,
|
|
3762
|
+
0.25,
|
|
3763
|
+
0.6,
|
|
3764
|
+
0,
|
|
3765
|
+
0,
|
|
3766
|
+
0,
|
|
3767
|
+
1,
|
|
3768
|
+
0.25,
|
|
3769
|
+
0.8,
|
|
3770
|
+
0,
|
|
3771
|
+
0,
|
|
3772
|
+
0,
|
|
3773
|
+
1,
|
|
3774
|
+
-0.45,
|
|
3775
|
+
0.6,
|
|
3776
|
+
0,
|
|
3777
|
+
0,
|
|
3778
|
+
0,
|
|
3779
|
+
1,
|
|
3780
|
+
-0.25,
|
|
3781
|
+
0.8,
|
|
3782
|
+
0,
|
|
3783
|
+
0,
|
|
3784
|
+
0,
|
|
3785
|
+
1,
|
|
3786
|
+
-0.25,
|
|
3787
|
+
0.8,
|
|
3788
|
+
0,
|
|
3789
|
+
0,
|
|
3790
|
+
0,
|
|
3791
|
+
1,
|
|
3792
|
+
-0.25,
|
|
3793
|
+
0.1,
|
|
3794
|
+
0,
|
|
3795
|
+
0,
|
|
3796
|
+
0,
|
|
3797
|
+
1,
|
|
3798
|
+
-0.25,
|
|
3799
|
+
0.1,
|
|
3800
|
+
0,
|
|
3801
|
+
0,
|
|
3802
|
+
0,
|
|
3803
|
+
1,
|
|
3804
|
+
-0.25,
|
|
3805
|
+
0.6,
|
|
3806
|
+
0,
|
|
3807
|
+
0,
|
|
3808
|
+
0,
|
|
3809
|
+
1,
|
|
3810
|
+
-0.45,
|
|
3811
|
+
0.1,
|
|
3812
|
+
0,
|
|
3813
|
+
0,
|
|
3814
|
+
0,
|
|
3815
|
+
1,
|
|
3816
|
+
-0.45,
|
|
3817
|
+
0.6,
|
|
3818
|
+
0,
|
|
3819
|
+
0,
|
|
3820
|
+
0,
|
|
3821
|
+
1,
|
|
3822
|
+
-0.45,
|
|
3823
|
+
0.6,
|
|
3824
|
+
0,
|
|
3825
|
+
0,
|
|
3826
|
+
0,
|
|
3827
|
+
1,
|
|
3828
|
+
0.25,
|
|
3829
|
+
-0.1,
|
|
3830
|
+
0,
|
|
3831
|
+
0,
|
|
3832
|
+
0,
|
|
3833
|
+
1,
|
|
3834
|
+
0.25,
|
|
3835
|
+
-0.1,
|
|
3836
|
+
0,
|
|
3837
|
+
0,
|
|
3838
|
+
0,
|
|
3839
|
+
1,
|
|
3840
|
+
0.25,
|
|
3841
|
+
0.1,
|
|
3842
|
+
0,
|
|
3843
|
+
0,
|
|
3844
|
+
0,
|
|
3845
|
+
1,
|
|
3846
|
+
-0.25,
|
|
3847
|
+
-0.1,
|
|
3848
|
+
0,
|
|
3849
|
+
0,
|
|
3850
|
+
0,
|
|
3851
|
+
1,
|
|
3852
|
+
-0.45,
|
|
3853
|
+
0.1,
|
|
3854
|
+
0,
|
|
3855
|
+
0,
|
|
3856
|
+
0,
|
|
3857
|
+
1,
|
|
3858
|
+
-0.45,
|
|
3859
|
+
0.1,
|
|
3860
|
+
0,
|
|
3861
|
+
0,
|
|
3862
|
+
0,
|
|
3863
|
+
1,
|
|
3864
|
+
-0.25,
|
|
3865
|
+
-0.8,
|
|
3866
|
+
0,
|
|
3867
|
+
0,
|
|
3868
|
+
0,
|
|
3869
|
+
1,
|
|
3870
|
+
-0.25,
|
|
3871
|
+
-0.8,
|
|
3872
|
+
0,
|
|
3873
|
+
0,
|
|
3874
|
+
0,
|
|
3875
|
+
1,
|
|
3876
|
+
-0.05,
|
|
3877
|
+
-0.1,
|
|
3878
|
+
0,
|
|
3879
|
+
0,
|
|
3880
|
+
0,
|
|
3881
|
+
1,
|
|
3882
|
+
-0.45,
|
|
3883
|
+
-0.8,
|
|
3884
|
+
0,
|
|
3885
|
+
0,
|
|
3886
|
+
0,
|
|
3887
|
+
1,
|
|
3888
|
+
-0.25,
|
|
3889
|
+
-0.1,
|
|
3890
|
+
0,
|
|
3891
|
+
0,
|
|
3892
|
+
0,
|
|
3893
|
+
1,
|
|
3894
|
+
-0.25,
|
|
3895
|
+
-0.1,
|
|
3896
|
+
0,
|
|
3897
|
+
0,
|
|
3898
|
+
0
|
|
3899
|
+
]);
|
|
2712
3900
|
var NiivueObject3D = function(id, vertexBuffer, mode, indexCount, indexBuffer = null, vao = null) {
|
|
2713
3901
|
this.BLEND = 1;
|
|
2714
3902
|
this.CULL_FACE = 2;
|
|
@@ -17201,11 +18389,11 @@ var jpx = { exports: {} };
|
|
|
17201
18389
|
}
|
|
17202
18390
|
return dict.get(name);
|
|
17203
18391
|
};
|
|
17204
|
-
Util2.inherit = function Util_inherit(
|
|
17205
|
-
|
|
17206
|
-
|
|
18392
|
+
Util2.inherit = function Util_inherit(sub2, base, prototype) {
|
|
18393
|
+
sub2.prototype = Object.create(base.prototype);
|
|
18394
|
+
sub2.prototype.constructor = sub2;
|
|
17207
18395
|
for (var prop in prototype) {
|
|
17208
|
-
|
|
18396
|
+
sub2.prototype[prop] = prototype[prop];
|
|
17209
18397
|
}
|
|
17210
18398
|
};
|
|
17211
18399
|
Util2.loadScript = function Util_loadScript(src, callback) {
|
|
@@ -103407,7 +104595,7 @@ function isPlatformLittleEndian() {
|
|
|
103407
104595
|
new DataView(buffer2).setInt16(0, 256, true);
|
|
103408
104596
|
return new Int16Array(buffer2)[0] === 256;
|
|
103409
104597
|
}
|
|
103410
|
-
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) {
|
|
104598
|
+
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 = "") {
|
|
103411
104599
|
this.DT_NONE = 0;
|
|
103412
104600
|
this.DT_UNKNOWN = 0;
|
|
103413
104601
|
this.DT_BINARY = 1;
|
|
@@ -103436,7 +104624,9 @@ function NVImage(dataBuffer, name = "", colorMap = "gray", opacity = 1, pairedIm
|
|
|
103436
104624
|
this.percentileFrac = percentileFrac;
|
|
103437
104625
|
this.ignoreZeroVoxels = ignoreZeroVoxels;
|
|
103438
104626
|
this.trustCalMinMax = trustCalMinMax;
|
|
104627
|
+
this.colorMapNegative = colorMapNegative;
|
|
103439
104628
|
this.visible = visible;
|
|
104629
|
+
this.modulationImage = null;
|
|
103440
104630
|
this.series = [];
|
|
103441
104631
|
if (!dataBuffer) {
|
|
103442
104632
|
return;
|
|
@@ -103700,7 +104890,25 @@ function NVImage(dataBuffer, name = "", colorMap = "gray", opacity = 1, pairedIm
|
|
|
103700
104890
|
this.hdr.cal_max = cal_max;
|
|
103701
104891
|
this.calMinMax();
|
|
103702
104892
|
}
|
|
104893
|
+
NVImage.prototype.computeObliqueAngle = function(mtx44) {
|
|
104894
|
+
let mtx = clone$1(mtx44);
|
|
104895
|
+
transpose(mtx, mtx44);
|
|
104896
|
+
let dxtmp = Math.sqrt(mtx[0] * mtx[0] + mtx[1] * mtx[1] + mtx[2] * mtx[2]);
|
|
104897
|
+
let xmax = Math.max(Math.max(Math.abs(mtx[0]), Math.abs(mtx[1])), Math.abs(mtx[2])) / dxtmp;
|
|
104898
|
+
let dytmp = Math.sqrt(mtx[4] * mtx[4] + mtx[5] * mtx[5] + mtx[6] * mtx[6]);
|
|
104899
|
+
let ymax = Math.max(Math.max(Math.abs(mtx[4]), Math.abs(mtx[5])), Math.abs(mtx[6])) / dytmp;
|
|
104900
|
+
let dztmp = Math.sqrt(mtx[8] * mtx[8] + mtx[9] * mtx[9] + mtx[10] * mtx[10]);
|
|
104901
|
+
let zmax = Math.max(Math.max(Math.abs(mtx[8]), Math.abs(mtx[9])), Math.abs(mtx[10])) / dztmp;
|
|
104902
|
+
let fig_merit = Math.min(Math.min(xmax, ymax), zmax);
|
|
104903
|
+
let oblique_angle = Math.abs(Math.acos(fig_merit) * 180 / 3.141592653);
|
|
104904
|
+
if (oblique_angle > 0.01)
|
|
104905
|
+
console.log("Warning voxels not aligned with world space: " + oblique_angle + " degrees from plumb.\n");
|
|
104906
|
+
else
|
|
104907
|
+
oblique_angle = 0;
|
|
104908
|
+
return oblique_angle;
|
|
104909
|
+
};
|
|
103703
104910
|
NVImage.prototype.calculateOblique = function() {
|
|
104911
|
+
this.oblique_angle = this.computeObliqueAngle(this.matRAS);
|
|
103704
104912
|
let LPI = this.vox2mm([0, 0, 0], this.matRAS);
|
|
103705
104913
|
let X1mm = this.vox2mm([1 / this.pixDimsRAS[1], 0, 0], this.matRAS);
|
|
103706
104914
|
let Y1mm = this.vox2mm([0, 1 / this.pixDimsRAS[2], 0], this.matRAS);
|
|
@@ -103716,6 +104924,21 @@ NVImage.prototype.calculateOblique = function() {
|
|
|
103716
104924
|
let maxShear = Math.max(Math.max(XY, XZ), YZ);
|
|
103717
104925
|
if (maxShear > 0.1)
|
|
103718
104926
|
log$2.debug("Warning: shear detected (gantry tilt) of %f degrees", maxShear);
|
|
104927
|
+
let dim = fromValues(this.dimsRAS[1], this.dimsRAS[2], this.dimsRAS[3], 1);
|
|
104928
|
+
let sform = clone$1(this.matRAS);
|
|
104929
|
+
transpose(sform, sform);
|
|
104930
|
+
let shim = fromValues(-0.5, -0.5, -0.5, 0);
|
|
104931
|
+
translate(sform, sform, shim);
|
|
104932
|
+
sform[0] *= dim[0];
|
|
104933
|
+
sform[1] *= dim[0];
|
|
104934
|
+
sform[2] *= dim[0];
|
|
104935
|
+
sform[4] *= dim[1];
|
|
104936
|
+
sform[5] *= dim[1];
|
|
104937
|
+
sform[6] *= dim[1];
|
|
104938
|
+
sform[8] *= dim[2];
|
|
104939
|
+
sform[9] *= dim[2];
|
|
104940
|
+
sform[10] *= dim[2];
|
|
104941
|
+
this.frac2mm = clone$1(sform);
|
|
103719
104942
|
};
|
|
103720
104943
|
NVImage.prototype.THD_daxes_to_NIFTI = function(xyzDelta, xyzOrigin, orientSpecific) {
|
|
103721
104944
|
let hdr = this.hdr;
|
|
@@ -104411,7 +105634,7 @@ NVImage.prototype.readMHA = function(buffer2, pairedImgData) {
|
|
|
104411
105634
|
line = readStr();
|
|
104412
105635
|
}
|
|
104413
105636
|
let mmMat = fromValues$3(hdr.pixDims[1], 0, 0, 0, hdr.pixDims[2], 0, 0, 0, hdr.pixDims[3]);
|
|
104414
|
-
multiply$
|
|
105637
|
+
multiply$1(mat33, mmMat, mat33);
|
|
104415
105638
|
hdr.affine = [
|
|
104416
105639
|
[-mat33[0], -mat33[3], -mat33[6], -offset[0]],
|
|
104417
105640
|
[-mat33[1], -mat33[4], -mat33[7], -offset[1]],
|
|
@@ -104812,7 +106035,7 @@ NVImage.prototype.readNRRD = function(dataBuffer, pairedImgData) {
|
|
|
104812
106035
|
offset[1] = -offset[1];
|
|
104813
106036
|
if (rot33[8] < 0)
|
|
104814
106037
|
offset[2] = -offset[2];
|
|
104815
|
-
multiply$
|
|
106038
|
+
multiply$1(mat33, rot33, mat33);
|
|
104816
106039
|
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);
|
|
104817
106040
|
let mm000 = this.vox2mm([0, 0, 0], mat);
|
|
104818
106041
|
let mm100 = this.vox2mm([1, 0, 0], mat);
|
|
@@ -104928,7 +106151,7 @@ NVImage.prototype.calculateRAS = function() {
|
|
|
104928
106151
|
rotM[3 + 2 * 4] = (header.dims[perm[2]] - 1) * flip[2];
|
|
104929
106152
|
let residualR = create$2();
|
|
104930
106153
|
invert(residualR, rotM);
|
|
104931
|
-
multiply
|
|
106154
|
+
multiply(residualR, residualR, R2);
|
|
104932
106155
|
this.matRAS = clone$1(residualR);
|
|
104933
106156
|
rotM = fromValues$2(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1);
|
|
104934
106157
|
rotM[perm[0] - 1 + 0 * 4] = -flip[0] * 2 + 1;
|
|
@@ -105230,7 +106453,8 @@ NVImage.loadFromUrl = async function({
|
|
|
105230
106453
|
trustCalMinMax = true,
|
|
105231
106454
|
percentileFrac = 0.02,
|
|
105232
106455
|
ignoreZeroVoxels = false,
|
|
105233
|
-
visible = true
|
|
106456
|
+
visible = true,
|
|
106457
|
+
colorMapNegative = ""
|
|
105234
106458
|
} = {}) {
|
|
105235
106459
|
if (url === "") {
|
|
105236
106460
|
throw Error("url must not be empty");
|
|
@@ -105263,7 +106487,7 @@ NVImage.loadFromUrl = async function({
|
|
|
105263
106487
|
pairedImgData = await resp.arrayBuffer();
|
|
105264
106488
|
}
|
|
105265
106489
|
if (dataBuffer) {
|
|
105266
|
-
nvimage = new NVImage(dataBuffer, name, colorMap, opacity, pairedImgData, cal_min, cal_max, trustCalMinMax, percentileFrac, ignoreZeroVoxels, visible);
|
|
106490
|
+
nvimage = new NVImage(dataBuffer, name, colorMap, opacity, pairedImgData, cal_min, cal_max, trustCalMinMax, percentileFrac, ignoreZeroVoxels, visible, false, false, colorMapNegative);
|
|
105267
106491
|
} else {
|
|
105268
106492
|
alert("Unable to load buffer properly from volume");
|
|
105269
106493
|
}
|
|
@@ -105463,14 +106687,20 @@ function getExtents$1(positions, forceOriginInVolume = true) {
|
|
|
105463
106687
|
return { min: min2, max: max2, furthestVertexFromOrigin, origin };
|
|
105464
106688
|
}
|
|
105465
106689
|
NVImage.prototype.toNiivueObject3D = function(id, gl) {
|
|
105466
|
-
let
|
|
105467
|
-
let
|
|
105468
|
-
let
|
|
105469
|
-
let
|
|
105470
|
-
let
|
|
105471
|
-
let
|
|
105472
|
-
let
|
|
105473
|
-
let
|
|
106690
|
+
let L = -0.5;
|
|
106691
|
+
let P = -0.5;
|
|
106692
|
+
let I2 = -0.5;
|
|
106693
|
+
let R2 = this.dimsRAS[1] - 1 + 0.5;
|
|
106694
|
+
let A2 = this.dimsRAS[2] - 1 + 0.5;
|
|
106695
|
+
let S = this.dimsRAS[3] - 1 + 0.5;
|
|
106696
|
+
let LPI = this.vox2mm([L, P, I2], this.matRAS);
|
|
106697
|
+
let LAI = this.vox2mm([L, A2, I2], this.matRAS);
|
|
106698
|
+
let LPS = this.vox2mm([L, P, S], this.matRAS);
|
|
106699
|
+
let LAS = this.vox2mm([L, A2, S], this.matRAS);
|
|
106700
|
+
let RPI = this.vox2mm([R2, P, I2], this.matRAS);
|
|
106701
|
+
let RAI = this.vox2mm([R2, A2, I2], this.matRAS);
|
|
106702
|
+
let RPS = this.vox2mm([R2, P, S], this.matRAS);
|
|
106703
|
+
let RAS = this.vox2mm([R2, A2, S], this.matRAS);
|
|
105474
106704
|
let posTex = [
|
|
105475
106705
|
...LPS,
|
|
105476
106706
|
...[0, 0, 1],
|
|
@@ -105553,11 +106783,16 @@ NVImage.prototype.toNiivueObject3D = function(id, gl) {
|
|
|
105553
106783
|
...RAI,
|
|
105554
106784
|
...RPI
|
|
105555
106785
|
]);
|
|
105556
|
-
obj3D.extentsMin = extents.min;
|
|
105557
|
-
obj3D.extentsMax = extents.max;
|
|
106786
|
+
obj3D.extentsMin = extents.min.slice();
|
|
106787
|
+
obj3D.extentsMax = extents.max.slice();
|
|
105558
106788
|
obj3D.furthestVertexFromOrigin = extents.furthestVertexFromOrigin;
|
|
105559
106789
|
obj3D.originNegate = clone(extents.origin);
|
|
105560
106790
|
negate(obj3D.originNegate, obj3D.originNegate);
|
|
106791
|
+
obj3D.fieldOfViewDeObliqueMM = [
|
|
106792
|
+
this.dimsRAS[1] * this.pixDimsRAS[1],
|
|
106793
|
+
this.dimsRAS[2] * this.pixDimsRAS[2],
|
|
106794
|
+
this.dimsRAS[3] * this.pixDimsRAS[3]
|
|
106795
|
+
];
|
|
105561
106796
|
return obj3D;
|
|
105562
106797
|
};
|
|
105563
106798
|
const colortables = function() {
|
|
@@ -105609,6 +106844,8 @@ colortables.prototype.makeLut = function(Rs, Gs, Bs, As, Is) {
|
|
|
105609
106844
|
if (this.gamma === 1)
|
|
105610
106845
|
return lut;
|
|
105611
106846
|
for (var i2 = 0; i2 < 255 * 4; i2++) {
|
|
106847
|
+
if (i2 % 4 === 3)
|
|
106848
|
+
continue;
|
|
105612
106849
|
lut[i2] = Math.pow(lut[i2] / 255, 1 / this.gamma) * 255;
|
|
105613
106850
|
}
|
|
105614
106851
|
return lut;
|
|
@@ -106282,7 +107519,7 @@ NVMesh.readTRK = function(buffer2) {
|
|
|
106282
107519
|
identity$1(mat);
|
|
106283
107520
|
}
|
|
106284
107521
|
var vox2mmMat = create$2();
|
|
106285
|
-
mul
|
|
107522
|
+
mul(vox2mmMat, mat, zoomMat);
|
|
106286
107523
|
let i32 = null;
|
|
106287
107524
|
let f32 = null;
|
|
106288
107525
|
i32 = new Int32Array(buffer2.slice(hdr_sz));
|
|
@@ -110233,11 +111470,13 @@ function Niivue(options = {}) {
|
|
|
110233
111470
|
textHeight: 0.06,
|
|
110234
111471
|
colorbarHeight: 0.05,
|
|
110235
111472
|
crosshairWidth: 1,
|
|
111473
|
+
rulerWidth: 4,
|
|
110236
111474
|
show3Dcrosshair: false,
|
|
110237
111475
|
backColor: [0, 0, 0, 1],
|
|
110238
111476
|
crosshairColor: [1, 0, 0, 1],
|
|
110239
111477
|
selectionBoxColor: [1, 1, 1, 0.5],
|
|
110240
111478
|
clipPlaneColor: [0.7, 0, 0.7, 0.5],
|
|
111479
|
+
rulerColor: [1, 0, 0, 0.8],
|
|
110241
111480
|
colorBarMargin: 0.05,
|
|
110242
111481
|
trustCalMinMax: true,
|
|
110243
111482
|
clipPlaneHotKey: "KeyC",
|
|
@@ -110245,7 +111484,17 @@ function Niivue(options = {}) {
|
|
|
110245
111484
|
keyDebounceTime: 50,
|
|
110246
111485
|
isNearestInterpolation: false,
|
|
110247
111486
|
isAtlasOutline: false,
|
|
111487
|
+
isRuler: false,
|
|
111488
|
+
isColorbar: false,
|
|
111489
|
+
isOrientCube: false,
|
|
111490
|
+
multiplanarPadPixels: 0,
|
|
110248
111491
|
isRadiologicalConvention: false,
|
|
111492
|
+
meshThicknessOn2D: Infinity,
|
|
111493
|
+
isDragShowsMeasurementTool: false,
|
|
111494
|
+
isDepthPickMesh: false,
|
|
111495
|
+
isCornerOrientationText: false,
|
|
111496
|
+
sagittalNoseLeft: false,
|
|
111497
|
+
isSliceMM: false,
|
|
110249
111498
|
logging: false,
|
|
110250
111499
|
loadingText: "waiting for images...",
|
|
110251
111500
|
dragAndDropEnabled: true,
|
|
@@ -110264,6 +111513,7 @@ function Niivue(options = {}) {
|
|
|
110264
111513
|
this.currentDrawUndoBitmap = this.maxDrawUndoBitmaps;
|
|
110265
111514
|
this.drawUndoBitmaps = [];
|
|
110266
111515
|
this.drawOpacity = 0.8;
|
|
111516
|
+
this.colorbarHeight = 0;
|
|
110267
111517
|
this.drawPenLocation = [NaN, NaN, NaN];
|
|
110268
111518
|
this.drawPenAxCorSag = -1;
|
|
110269
111519
|
this.drawFillOverwrites = true;
|
|
@@ -110271,10 +111521,14 @@ function Niivue(options = {}) {
|
|
|
110271
111521
|
this.overlayTexture = null;
|
|
110272
111522
|
this.overlayTextureID = [];
|
|
110273
111523
|
this.sliceShader = null;
|
|
110274
|
-
this.
|
|
111524
|
+
this.sliceMMShader = null;
|
|
111525
|
+
this.orientCubeShader = null;
|
|
111526
|
+
this.orientCubeShaderVAO = null;
|
|
111527
|
+
this.rectShader = null;
|
|
110275
111528
|
this.graphShader = null;
|
|
110276
111529
|
this.renderShader = null;
|
|
110277
|
-
this.
|
|
111530
|
+
this.pickingMeshShader = null;
|
|
111531
|
+
this.pickingImageShader = null;
|
|
110278
111532
|
this.colorbarShader = null;
|
|
110279
111533
|
this.fontShader = null;
|
|
110280
111534
|
this.fontTexture = null;
|
|
@@ -110294,7 +111548,6 @@ function Niivue(options = {}) {
|
|
|
110294
111548
|
this.genericVAO = null;
|
|
110295
111549
|
this.unusedVAO = null;
|
|
110296
111550
|
this.crosshairs3D = null;
|
|
110297
|
-
this.pickingSurfaceShader = null;
|
|
110298
111551
|
this.DEFAULT_FONT_GLYPH_SHEET = defaultFontPNG;
|
|
110299
111552
|
this.DEFAULT_FONT_METRICS = defaultFontMetrics;
|
|
110300
111553
|
this.fontMets = null;
|
|
@@ -110304,6 +111557,7 @@ function Niivue(options = {}) {
|
|
|
110304
111557
|
this.sliceTypeSagittal = 2;
|
|
110305
111558
|
this.sliceTypeMultiplanar = 3;
|
|
110306
111559
|
this.sliceTypeRender = 4;
|
|
111560
|
+
this.sliceMosaicString = "";
|
|
110307
111561
|
this.sliceType = this.sliceTypeMultiplanar;
|
|
110308
111562
|
this.scene = {};
|
|
110309
111563
|
this.syncOpts = {};
|
|
@@ -110320,6 +111574,7 @@ function Niivue(options = {}) {
|
|
|
110320
111574
|
this.scene.mouseButtonLeftDown = false;
|
|
110321
111575
|
this.scene.mouseButtonRightDown = false;
|
|
110322
111576
|
this.scene.mouseDepthPicker = false;
|
|
111577
|
+
this.scene.pan2Dxyzmm = [0, 0, 0, 1];
|
|
110323
111578
|
this.scene.scale2D = 1;
|
|
110324
111579
|
this.scene.prevX = 0;
|
|
110325
111580
|
this.scene.prevY = 0;
|
|
@@ -110336,13 +111591,7 @@ function Niivue(options = {}) {
|
|
|
110336
111591
|
this.volScale = [];
|
|
110337
111592
|
this.vox = [];
|
|
110338
111593
|
this.mousePos = [0, 0];
|
|
110339
|
-
this.
|
|
110340
|
-
this.screenSlices = [
|
|
110341
|
-
{ leftTopWidthHeight: [1, 0, 0, 1], axCorSag: this.sliceTypeAxial },
|
|
110342
|
-
{ leftTopWidthHeight: [1, 0, 0, 1], axCorSag: this.sliceTypeAxial },
|
|
110343
|
-
{ leftTopWidthHeight: [1, 0, 0, 1], axCorSag: this.sliceTypeAxial },
|
|
110344
|
-
{ leftTopWidthHeight: [1, 0, 0, 1], axCorSag: this.sliceTypeAxial }
|
|
110345
|
-
];
|
|
111594
|
+
this.screenSlices = [];
|
|
110346
111595
|
this.isDragging = false;
|
|
110347
111596
|
this.dragStart = [0, 0];
|
|
110348
111597
|
this.dragEnd = [0, 0];
|
|
@@ -110394,6 +111643,10 @@ function Niivue(options = {}) {
|
|
|
110394
111643
|
{
|
|
110395
111644
|
Name: "Toon",
|
|
110396
111645
|
Frag: fragMeshToonShader
|
|
111646
|
+
},
|
|
111647
|
+
{
|
|
111648
|
+
Name: "Depth",
|
|
111649
|
+
Frag: fragMeshDepthShader
|
|
110397
111650
|
}
|
|
110398
111651
|
];
|
|
110399
111652
|
this.isController = false;
|
|
@@ -110422,6 +111675,28 @@ function Niivue(options = {}) {
|
|
|
110422
111675
|
};
|
|
110423
111676
|
this.subscriptions = [];
|
|
110424
111677
|
}
|
|
111678
|
+
Niivue.prototype.saveScene = function(filename = "") {
|
|
111679
|
+
function saveBlob(blob, name) {
|
|
111680
|
+
const a = document.createElement("a");
|
|
111681
|
+
document.body.appendChild(a);
|
|
111682
|
+
a.style.display = "none";
|
|
111683
|
+
const url = window.URL.createObjectURL(blob);
|
|
111684
|
+
a.href = url;
|
|
111685
|
+
a.download = name;
|
|
111686
|
+
a.click();
|
|
111687
|
+
a.remove();
|
|
111688
|
+
}
|
|
111689
|
+
let canvas = this.canvas;
|
|
111690
|
+
this.drawScene();
|
|
111691
|
+
canvas.toBlob((blob) => {
|
|
111692
|
+
if (filename === "") {
|
|
111693
|
+
filename = `niivue-screenshot-${new Date().toString()}.png`;
|
|
111694
|
+
filename = filename.replace(/\s/g, "_");
|
|
111695
|
+
}
|
|
111696
|
+
console.log(filename);
|
|
111697
|
+
saveBlob(blob, filename);
|
|
111698
|
+
});
|
|
111699
|
+
};
|
|
110425
111700
|
Niivue.prototype.attachTo = async function(id) {
|
|
110426
111701
|
await this.attachToCanvas(document.getElementById(id));
|
|
110427
111702
|
log.debug("attached to element with id: ", id);
|
|
@@ -110640,14 +111915,18 @@ function intensityRaw2Scaled(hdr, raw) {
|
|
|
110640
111915
|
return raw * hdr.scl_slope + hdr.scl_inter;
|
|
110641
111916
|
}
|
|
110642
111917
|
Niivue.prototype.calculateNewRange = function(volIdx = 0) {
|
|
110643
|
-
if (this.sliceType === this.sliceTypeRender) {
|
|
111918
|
+
if (this.sliceType === this.sliceTypeRender && this.sliceMosaicString.length < 1) {
|
|
110644
111919
|
return;
|
|
110645
111920
|
}
|
|
110646
111921
|
if (this.dragStart[0] === this.dragEnd[0] && this.dragStart[1] === this.dragEnd[1])
|
|
110647
111922
|
return;
|
|
110648
111923
|
let frac = this.canvasPos2frac([this.dragStart[0], this.dragStart[1]]);
|
|
111924
|
+
if (frac[0] < 0)
|
|
111925
|
+
return;
|
|
110649
111926
|
let startVox = this.frac2vox(frac, volIdx);
|
|
110650
111927
|
frac = this.canvasPos2frac([this.dragEnd[0], this.dragEnd[1]]);
|
|
111928
|
+
if (frac[0] < 0)
|
|
111929
|
+
return;
|
|
110651
111930
|
let endVox = this.frac2vox(frac, volIdx);
|
|
110652
111931
|
let hi = -Number.MAX_VALUE, lo = Number.MAX_VALUE;
|
|
110653
111932
|
let xrange;
|
|
@@ -110702,6 +111981,8 @@ Niivue.prototype.mouseUpListener = function() {
|
|
|
110702
111981
|
this.drawPenAxCorSag = -1;
|
|
110703
111982
|
if (this.isDragging) {
|
|
110704
111983
|
this.isDragging = false;
|
|
111984
|
+
if (this.opts.isDragShowsMeasurementTool)
|
|
111985
|
+
return;
|
|
110705
111986
|
this.calculateNewRange();
|
|
110706
111987
|
this.refreshLayers(this.volumes[0], 0, this.volumes.length);
|
|
110707
111988
|
}
|
|
@@ -110744,10 +112025,18 @@ Niivue.prototype.mouseMoveListener = function(e) {
|
|
|
110744
112025
|
this.scene.prevY = this.scene.currY;
|
|
110745
112026
|
}
|
|
110746
112027
|
};
|
|
110747
|
-
Niivue.prototype.resetBriCon = function() {
|
|
110748
|
-
|
|
112028
|
+
Niivue.prototype.resetBriCon = function(msg2 = null) {
|
|
112029
|
+
let isRender = false;
|
|
112030
|
+
if (this.sliceType === this.sliceTypeRender)
|
|
112031
|
+
isRender = true;
|
|
112032
|
+
if (msg2 !== null) {
|
|
112033
|
+
if (this.inRenderTile(msg2.offsetX, msg2.offsetY) >= 0)
|
|
112034
|
+
isRender = true;
|
|
112035
|
+
}
|
|
112036
|
+
if (isRender) {
|
|
110749
112037
|
this.scene.mouseDepthPicker = true;
|
|
110750
112038
|
this.drawScene();
|
|
112039
|
+
this.drawScene();
|
|
110751
112040
|
return;
|
|
110752
112041
|
}
|
|
110753
112042
|
this.volumes[0].cal_min = this.volumes[0].robust_min;
|
|
@@ -110783,9 +112072,6 @@ Niivue.prototype.handlePinchZoom = function(e) {
|
|
|
110783
112072
|
};
|
|
110784
112073
|
Niivue.prototype.keyUpListener = function(e) {
|
|
110785
112074
|
if (e.code === this.opts.clipPlaneHotKey) {
|
|
110786
|
-
if (this.sliceType != this.sliceTypeRender) {
|
|
110787
|
-
return;
|
|
110788
|
-
}
|
|
110789
112075
|
let now = new Date().getTime();
|
|
110790
112076
|
let elapsed = now - this.lastCalled;
|
|
110791
112077
|
if (elapsed > this.opts.keyDebounceTime) {
|
|
@@ -110983,8 +112269,26 @@ Niivue.prototype.dropListener = async function(e) {
|
|
|
110983
112269
|
}
|
|
110984
112270
|
this.drawScene();
|
|
110985
112271
|
};
|
|
112272
|
+
Niivue.prototype.setCornerOrientationText = function(isCornerOrientationText) {
|
|
112273
|
+
this.opts.isCornerOrientationText = isCornerOrientationText;
|
|
112274
|
+
this.updateGLVolume();
|
|
112275
|
+
};
|
|
110986
112276
|
Niivue.prototype.setRadiologicalConvention = function(isRadiologicalConvention) {
|
|
110987
112277
|
this.opts.isRadiologicalConvention = isRadiologicalConvention;
|
|
112278
|
+
this.updateGLVolume();
|
|
112279
|
+
};
|
|
112280
|
+
Niivue.prototype.setMeshThicknessOn2D = function(meshThicknessOn2D) {
|
|
112281
|
+
this.opts.meshThicknessOn2D = meshThicknessOn2D;
|
|
112282
|
+
this.updateGLVolume();
|
|
112283
|
+
};
|
|
112284
|
+
Niivue.prototype.setSliceMosaicString = function(str) {
|
|
112285
|
+
this.sliceMosaicString = str;
|
|
112286
|
+
this.updateGLVolume();
|
|
112287
|
+
};
|
|
112288
|
+
Niivue.prototype.setSliceMM = function(isSliceMM) {
|
|
112289
|
+
this.opts.isSliceMM = isSliceMM;
|
|
112290
|
+
console.log("SliceMM feature is experimental");
|
|
112291
|
+
this.updateGLVolume();
|
|
110988
112292
|
};
|
|
110989
112293
|
Niivue.prototype.getRadiologicalConvention = function() {
|
|
110990
112294
|
return this.opts.isRadiologicalConvention;
|
|
@@ -111291,9 +112595,12 @@ Niivue.prototype.setMeshLayerProperty = function(mesh, layer, key2, val) {
|
|
|
111291
112595
|
this.meshes[idx].setLayerProperty(layer, key2, val, this.gl);
|
|
111292
112596
|
this.updateGLVolume();
|
|
111293
112597
|
};
|
|
112598
|
+
Niivue.prototype.setPan2Dxyzmm = function(xyzmmZoom) {
|
|
112599
|
+
this.scene.pan2Dxyzmm = xyzmmZoom;
|
|
112600
|
+
this.drawScene();
|
|
112601
|
+
};
|
|
111294
112602
|
Niivue.prototype.setScale2D = function(zoomFactor) {
|
|
111295
112603
|
this.scene.scale2D = zoomFactor;
|
|
111296
|
-
console.log("bingo");
|
|
111297
112604
|
this.drawScene();
|
|
111298
112605
|
};
|
|
111299
112606
|
Niivue.prototype.setRenderAzimuthElevation = function(a, e) {
|
|
@@ -111388,12 +112695,12 @@ Niivue.prototype.moveVolumeToTop = function(volume) {
|
|
|
111388
112695
|
this.setVolume(volume, this.volumes.length - 1);
|
|
111389
112696
|
};
|
|
111390
112697
|
Niivue.prototype.mouseDown = function mouseDown(x2, y) {
|
|
111391
|
-
if (this.
|
|
112698
|
+
if (this.inRenderTile(x2, y) < 0)
|
|
111392
112699
|
return;
|
|
111393
112700
|
this.mousePos = [x2, y];
|
|
111394
112701
|
};
|
|
111395
112702
|
Niivue.prototype.mouseMove = function mouseMove(x2, y) {
|
|
111396
|
-
if (this.
|
|
112703
|
+
if (this.inRenderTile(x2, y) < 0)
|
|
111397
112704
|
return;
|
|
111398
112705
|
this.scene.renderAzimuth += x2 - this.mousePos[0];
|
|
111399
112706
|
this.scene.renderElevation += y - this.mousePos[1];
|
|
@@ -111420,8 +112727,6 @@ Niivue.prototype.setClipPlane = function(depthAzimuthElevation) {
|
|
|
111420
112727
|
let v = this.sph2cartDeg(depthAzimuthElevation[1] + 180, depthAzimuthElevation[2]);
|
|
111421
112728
|
this.scene.clipPlane = [v[0], v[1], v[2], depthAzimuthElevation[0]];
|
|
111422
112729
|
this.scene.clipPlaneDepthAziElev = depthAzimuthElevation;
|
|
111423
|
-
if (this.sliceType != this.sliceTypeRender)
|
|
111424
|
-
return;
|
|
111425
112730
|
this.drawScene();
|
|
111426
112731
|
};
|
|
111427
112732
|
Niivue.prototype.setCrosshairColor = function(color) {
|
|
@@ -111538,6 +112843,7 @@ Niivue.prototype.loadVolumes = async function(volumeList) {
|
|
|
111538
112843
|
url: volumeList[i2].url,
|
|
111539
112844
|
name: volumeList[i2].name,
|
|
111540
112845
|
colorMap: volumeList[i2].colorMap,
|
|
112846
|
+
colorMapNegative: volumeList[i2].colorMapNegative,
|
|
111541
112847
|
opacity: volumeList[i2].opacity,
|
|
111542
112848
|
urlImgData: volumeList[i2].urlImgData,
|
|
111543
112849
|
cal_min: volumeList[i2].cal_min,
|
|
@@ -111806,7 +113112,7 @@ Niivue.prototype.drawPt = function(x2, y, z, penValue) {
|
|
|
111806
113112
|
z = Math.min(Math.max(z, 0), dz - 1);
|
|
111807
113113
|
this.drawBitmap[x2 + y * dx + z * dx * dy] = penValue;
|
|
111808
113114
|
};
|
|
111809
|
-
Niivue.prototype.
|
|
113115
|
+
Niivue.prototype.drawPenLine = function(ptA, ptB, penValue) {
|
|
111810
113116
|
let dx = Math.abs(ptA[0] - ptB[0]);
|
|
111811
113117
|
let dy = Math.abs(ptA[1] - ptB[1]);
|
|
111812
113118
|
let dz = Math.abs(ptA[2] - ptB[2]);
|
|
@@ -112302,21 +113608,44 @@ Niivue.prototype.init = async function() {
|
|
|
112302
113608
|
this.gl.enableVertexAttribArray(0);
|
|
112303
113609
|
this.gl.vertexAttribPointer(0, 3, this.gl.FLOAT, false, 0, 0);
|
|
112304
113610
|
this.gl.bindVertexArray(this.unusedVAO);
|
|
112305
|
-
this.
|
|
112306
|
-
this.
|
|
112307
|
-
this.
|
|
112308
|
-
this.gl.uniform1i(this.
|
|
112309
|
-
this.
|
|
112310
|
-
this.
|
|
112311
|
-
this.
|
|
113611
|
+
this.pickingMeshShader = new Shader(this.gl, vertMeshShader, fragMeshDepthShader);
|
|
113612
|
+
this.pickingImageShader = new Shader(this.gl, vertRenderShader, fragVolumePickingShader);
|
|
113613
|
+
this.pickingImageShader.use(this.gl);
|
|
113614
|
+
this.gl.uniform1i(this.pickingImageShader.uniforms["volume"], 0);
|
|
113615
|
+
this.gl.uniform1i(this.pickingImageShader.uniforms["overlay"], 2);
|
|
113616
|
+
this.pickingImageShader.mvpUniformLoc = this.pickingImageShader.uniforms["mvpMtx"];
|
|
113617
|
+
this.pickingImageShader.rayDirUniformLoc = this.pickingImageShader.uniforms["rayDir"];
|
|
113618
|
+
this.pickingImageShader.clipPlaneUniformLoc = this.pickingImageShader.uniforms["clipPlane"];
|
|
112312
113619
|
this.sliceShader = new Shader(this.gl, vertSliceShader, fragSliceShader);
|
|
112313
113620
|
this.sliceShader.use(this.gl);
|
|
112314
113621
|
this.gl.uniform1i(this.sliceShader.uniforms["volume"], 0);
|
|
112315
113622
|
this.gl.uniform1i(this.sliceShader.uniforms["overlay"], 2);
|
|
112316
113623
|
this.gl.uniform1i(this.sliceShader.uniforms["drawing"], 7);
|
|
112317
113624
|
this.gl.uniform1f(this.sliceShader.uniforms["drawOpacity"], this.drawOpacity);
|
|
112318
|
-
this.
|
|
112319
|
-
this.
|
|
113625
|
+
this.sliceMMShader = new Shader(this.gl, vertSliceMMShader, fragSliceMMShader);
|
|
113626
|
+
this.sliceMMShader.use(this.gl);
|
|
113627
|
+
this.gl.uniform1i(this.sliceMMShader.uniforms["volume"], 0);
|
|
113628
|
+
this.gl.uniform1i(this.sliceMMShader.uniforms["overlay"], 2);
|
|
113629
|
+
this.gl.uniform1i(this.sliceMMShader.uniforms["drawing"], 7);
|
|
113630
|
+
this.gl.uniform1f(this.sliceMMShader.uniforms["drawOpacity"], this.drawOpacity);
|
|
113631
|
+
this.orientCubeShader = new Shader(this.gl, vertOrientCubeShader, fragOrientCubeShader);
|
|
113632
|
+
let gl = this.gl;
|
|
113633
|
+
this.orientCubeShaderVAO = gl.createVertexArray();
|
|
113634
|
+
gl.bindVertexArray(this.orientCubeShaderVAO);
|
|
113635
|
+
let program = this.orientCubeShader.program;
|
|
113636
|
+
this.orientCubeMtxLoc = gl.getUniformLocation(program, "u_matrix");
|
|
113637
|
+
var positionBuffer = gl.createBuffer();
|
|
113638
|
+
gl.enableVertexAttribArray(0);
|
|
113639
|
+
gl.enableVertexAttribArray(1);
|
|
113640
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
113641
|
+
gl.bufferData(gl.ARRAY_BUFFER, orientCube, gl.STATIC_DRAW);
|
|
113642
|
+
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 24, 0);
|
|
113643
|
+
gl.enableVertexAttribArray(1);
|
|
113644
|
+
gl.vertexAttribPointer(1, 3, gl.FLOAT, false, 24, 12);
|
|
113645
|
+
gl.bindVertexArray(this.unusedVAO);
|
|
113646
|
+
this.rectShader = new Shader(this.gl, vertRectShader, fragRectShader);
|
|
113647
|
+
this.lineShader = new Shader(this.gl, vertLineShader, fragRectShader);
|
|
113648
|
+
this.graphShader = new Shader(this.gl, vertGraphShader, fragRectShader);
|
|
112320
113649
|
this.renderShader = new Shader(this.gl, vertRenderShader, fragRenderShader);
|
|
112321
113650
|
this.renderShader.use(this.gl);
|
|
112322
113651
|
this.gl.uniform1i(this.renderShader.uniforms["volume"], 0);
|
|
@@ -112332,7 +113661,6 @@ Niivue.prototype.init = async function() {
|
|
|
112332
113661
|
this.orientShaderI = new Shader(this.gl, vertOrientShader, fragOrientShaderI.concat(fragOrientShader));
|
|
112333
113662
|
this.orientShaderF = new Shader(this.gl, vertOrientShader, fragOrientShaderF.concat(fragOrientShader));
|
|
112334
113663
|
this.orientShaderRGBU = new Shader(this.gl, vertOrientShader, fragOrientShaderU.concat(fragRGBOrientShader));
|
|
112335
|
-
this.pickingSurfaceShader = new Shader(this.gl, vertRenderShader, fragDepthPickingShader);
|
|
112336
113664
|
this.surfaceShader = new Shader(this.gl, vertSurfaceShader, fragSurfaceShader);
|
|
112337
113665
|
this.fiberShader = new Shader(this.gl, vertFiberShader, fragFiberShader);
|
|
112338
113666
|
this.meshShader = new Shader(this.gl, vertMeshShader, this.meshShaders[0].Frag);
|
|
@@ -112444,7 +113772,6 @@ Niivue.prototype.refreshLayers = function(overlayItem, layer, numLayers) {
|
|
|
112444
113772
|
if (layer === 0) {
|
|
112445
113773
|
this.volumeObject3D = overlayItem.toNiivueObject3D(this.VOLUME_ID, this.gl);
|
|
112446
113774
|
invert(mtx, mtx);
|
|
112447
|
-
log.debug(`mtx layer ${layer}`, mtx);
|
|
112448
113775
|
this.back.matRAS = overlayItem.matRAS;
|
|
112449
113776
|
this.back.dims = overlayItem.dimsRAS;
|
|
112450
113777
|
this.back.pixDims = overlayItem.pixDimsRAS;
|
|
@@ -112456,8 +113783,7 @@ Niivue.prototype.refreshLayers = function(overlayItem, layer, numLayers) {
|
|
|
112456
113783
|
this.renderShader.use(this.gl);
|
|
112457
113784
|
this.gl.uniform3fv(this.renderShader.uniforms["texVox"], vox2);
|
|
112458
113785
|
this.gl.uniform3fv(this.renderShader.uniforms["volScale"], volScale2);
|
|
112459
|
-
this.
|
|
112460
|
-
let pickingShader = this.pickingShader;
|
|
113786
|
+
let pickingShader = this.pickingImageShader;
|
|
112461
113787
|
pickingShader.use(this.gl);
|
|
112462
113788
|
this.gl.uniform1i(pickingShader.uniforms["volume"], 0);
|
|
112463
113789
|
this.gl.uniform1i(pickingShader.uniforms["colormap"], 1);
|
|
@@ -112561,10 +113887,54 @@ Niivue.prototype.refreshLayers = function(overlayItem, layer, numLayers) {
|
|
|
112561
113887
|
this.gl.uniform1i(orientShader.uniforms["blend3D"], 5);
|
|
112562
113888
|
this.gl.uniform1i(orientShader.uniforms["colormap"], 1);
|
|
112563
113889
|
this.gl.uniform1f(orientShader.uniforms["layer"], layer);
|
|
112564
|
-
this.gl.uniform1f(orientShader.uniforms["numLayers"], numLayers);
|
|
112565
113890
|
this.gl.uniform1f(orientShader.uniforms["scl_inter"], hdr.scl_inter);
|
|
112566
113891
|
this.gl.uniform1f(orientShader.uniforms["scl_slope"], hdr.scl_slope);
|
|
112567
113892
|
this.gl.uniform1f(orientShader.uniforms["opacity"], opacity);
|
|
113893
|
+
this.gl.uniform1i(orientShader.uniforms["modulationVol"], 7);
|
|
113894
|
+
let modulateTexture = null;
|
|
113895
|
+
if (overlayItem.modulationImage && overlayItem.modulationImage >= 0 && overlayItem.modulationImage < this.volumes.length) {
|
|
113896
|
+
console.log(this.volumes);
|
|
113897
|
+
let mhdr = this.volumes[overlayItem.modulationImage].hdr;
|
|
113898
|
+
if (mhdr.dims[1] === hdr.dims[1] && mhdr.dims[2] === hdr.dims[2] && mhdr.dims[3] === hdr.dims[3]) {
|
|
113899
|
+
this.gl.uniform1i(orientShader.uniforms["modulation"], 1);
|
|
113900
|
+
modulateTexture = this.r8Tex(modulateTexture, this.gl.TEXTURE7, hdr.dims, true);
|
|
113901
|
+
this.gl.activeTexture(this.gl.TEXTURE7);
|
|
113902
|
+
this.gl.bindTexture(this.gl.TEXTURE_3D, modulateTexture);
|
|
113903
|
+
let vx = hdr.dims[1] * hdr.dims[2] * hdr.dims[3];
|
|
113904
|
+
let modulateVolume = new Uint8Array(vx);
|
|
113905
|
+
let mn = mhdr.cal_min;
|
|
113906
|
+
let scale2 = 255 / (mhdr.cal_max - mhdr.cal_min);
|
|
113907
|
+
let imgRaw = this.volumes[overlayItem.modulationImage].img.buffer;
|
|
113908
|
+
let img2 = new Uint8Array(imgRaw);
|
|
113909
|
+
switch (mhdr.datatypeCode) {
|
|
113910
|
+
case overlayItem.DT_SIGNED_SHORT:
|
|
113911
|
+
img2 = new Int16Array(imgRaw);
|
|
113912
|
+
break;
|
|
113913
|
+
case overlayItem.DT_FLOAT:
|
|
113914
|
+
img2 = new Float32Array(imgRaw);
|
|
113915
|
+
break;
|
|
113916
|
+
case overlayItem.DT_DOUBLE:
|
|
113917
|
+
img2 = new Float64Array(imgRaw);
|
|
113918
|
+
break;
|
|
113919
|
+
case overlayItem.DT_RGB:
|
|
113920
|
+
img2 = new Uint8Array(imgRaw);
|
|
113921
|
+
break;
|
|
113922
|
+
case overlayItem.DT_UINT16:
|
|
113923
|
+
img2 = new Uint16Array(imgRaw);
|
|
113924
|
+
break;
|
|
113925
|
+
}
|
|
113926
|
+
console.log(this.volumes[overlayItem.modulationImage]);
|
|
113927
|
+
for (let i2 = 0; i2 < vx; i2++) {
|
|
113928
|
+
let v = img2[i2] * mhdr.scl_slope + mhdr.scl_inter;
|
|
113929
|
+
v = (v - mn) * scale2;
|
|
113930
|
+
v = Math.min(Math.max(v, 0), 255);
|
|
113931
|
+
modulateVolume[i2] = v;
|
|
113932
|
+
}
|
|
113933
|
+
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);
|
|
113934
|
+
} else
|
|
113935
|
+
console.log("Modulation image dimensions do not match target");
|
|
113936
|
+
} else
|
|
113937
|
+
this.gl.uniform1i(orientShader.uniforms["modulation"], 0);
|
|
112568
113938
|
this.gl.uniformMatrix4fv(orientShader.uniforms["mtx"], false, mtx);
|
|
112569
113939
|
if (hdr.intent_code === 1002) {
|
|
112570
113940
|
let x2 = 1 / this.back.dims[1];
|
|
@@ -112585,6 +113955,7 @@ Niivue.prototype.refreshLayers = function(overlayItem, layer, numLayers) {
|
|
|
112585
113955
|
}
|
|
112586
113956
|
this.gl.bindVertexArray(this.unusedVAO);
|
|
112587
113957
|
this.gl.deleteTexture(tempTex3D);
|
|
113958
|
+
this.gl.deleteTexture(modulateTexture);
|
|
112588
113959
|
this.gl.deleteTexture(blendTexture);
|
|
112589
113960
|
this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
|
|
112590
113961
|
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);
|
|
@@ -112599,13 +113970,19 @@ Niivue.prototype.refreshLayers = function(overlayItem, layer, numLayers) {
|
|
|
112599
113970
|
this.gl.uniform4fv(this.renderShader.uniforms["clipPlane"], this.scene.clipPlane);
|
|
112600
113971
|
this.gl.uniform3fv(this.renderShader.uniforms["texVox"], vox);
|
|
112601
113972
|
this.gl.uniform3fv(this.renderShader.uniforms["volScale"], volScale);
|
|
112602
|
-
this.
|
|
112603
|
-
this.gl.uniform1f(this.
|
|
112604
|
-
this.gl.uniform3fv(this.
|
|
113973
|
+
this.pickingImageShader.use(this.gl);
|
|
113974
|
+
this.gl.uniform1f(this.pickingImageShader.uniforms["overlays"], this.overlays.length);
|
|
113975
|
+
this.gl.uniform3fv(this.pickingImageShader.uniforms["texVox"], vox);
|
|
113976
|
+
this.sliceMMShader.use(this.gl);
|
|
113977
|
+
this.gl.uniform1f(this.sliceMMShader.uniforms["overlays"], this.overlays.length);
|
|
113978
|
+
this.gl.uniform1f(this.sliceMMShader.uniforms["drawOpacity"], this.drawOpacity);
|
|
113979
|
+
this.gl.uniform1i(this.sliceMMShader.uniforms["drawing"], 7);
|
|
112605
113980
|
this.sliceShader.use(this.gl);
|
|
112606
113981
|
this.gl.uniform1f(this.sliceShader.uniforms["overlays"], this.overlays.length);
|
|
112607
113982
|
this.gl.uniform1f(this.sliceShader.uniforms["drawOpacity"], this.drawOpacity);
|
|
112608
113983
|
this.gl.uniform1i(this.sliceShader.uniforms["drawing"], 7);
|
|
113984
|
+
this.gl.activeTexture(this.gl.TEXTURE7);
|
|
113985
|
+
this.gl.bindTexture(this.gl.TEXTURE_3D, this.drawTexture);
|
|
112609
113986
|
this.updateInterpolation(layer);
|
|
112610
113987
|
};
|
|
112611
113988
|
Niivue.prototype.colorMaps = function(sort = true) {
|
|
@@ -112616,6 +113993,18 @@ Niivue.prototype.setColorMap = function(id, colorMap) {
|
|
|
112616
113993
|
this.volumes[idx].colorMap = colorMap;
|
|
112617
113994
|
this.updateGLVolume();
|
|
112618
113995
|
};
|
|
113996
|
+
Niivue.prototype.setColorMapNegative = function(id, colorMapNegative) {
|
|
113997
|
+
let idx = this.getVolumeIndexByID(id);
|
|
113998
|
+
this.volumes[idx].colorMapNegative = colorMapNegative;
|
|
113999
|
+
this.updateGLVolume();
|
|
114000
|
+
};
|
|
114001
|
+
Niivue.prototype.setModulationImage = function(idTarget, idModulation) {
|
|
114002
|
+
let idxTarget = this.getVolumeIndexByID(idTarget);
|
|
114003
|
+
let idxModulation = null;
|
|
114004
|
+
idxModulation = this.getVolumeIndexByID(idModulation);
|
|
114005
|
+
this.volumes[idxTarget].modulationImage = idxModulation;
|
|
114006
|
+
this.updateGLVolume();
|
|
114007
|
+
};
|
|
112619
114008
|
Niivue.prototype.setGamma = function(gamma = 1) {
|
|
112620
114009
|
cmapper.gamma = gamma;
|
|
112621
114010
|
this.updateGLVolume();
|
|
@@ -112650,40 +114039,87 @@ Niivue.prototype.refreshColormaps = function() {
|
|
|
112650
114039
|
this.colormapTexture = this.gl.createTexture();
|
|
112651
114040
|
this.gl.activeTexture(this.gl.TEXTURE1);
|
|
112652
114041
|
this.gl.bindTexture(this.gl.TEXTURE_2D, this.colormapTexture);
|
|
112653
|
-
this.gl.texStorage2D(this.gl.TEXTURE_2D, 1, this.gl.RGBA8, 256, nLayer);
|
|
114042
|
+
this.gl.texStorage2D(this.gl.TEXTURE_2D, 1, this.gl.RGBA8, 256, nLayer * 2);
|
|
112654
114043
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
|
|
112655
114044
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
|
|
112656
114045
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_R, this.gl.CLAMP_TO_EDGE);
|
|
112657
114046
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
|
|
112658
114047
|
this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT, 1);
|
|
112659
114048
|
let luts2 = this.colormap(this.volumes[0].colorMap);
|
|
112660
|
-
|
|
112661
|
-
|
|
112662
|
-
|
|
112663
|
-
|
|
112664
|
-
|
|
112665
|
-
|
|
112666
|
-
|
|
112667
|
-
|
|
112668
|
-
|
|
112669
|
-
|
|
114049
|
+
function addColormap(lut) {
|
|
114050
|
+
let c2 = new Uint8ClampedArray(luts2.length + lut.length);
|
|
114051
|
+
c2.set(luts2);
|
|
114052
|
+
c2.set(lut, luts2.length);
|
|
114053
|
+
luts2 = c2;
|
|
114054
|
+
}
|
|
114055
|
+
for (let i2 = 1; i2 < nLayer; i2++)
|
|
114056
|
+
addColormap(this.colormap(this.volumes[i2].colorMap));
|
|
114057
|
+
let bogusLut = new Uint8ClampedArray(1024);
|
|
114058
|
+
bogusLut[0] = 255;
|
|
114059
|
+
bogusLut[1] = 255;
|
|
114060
|
+
bogusLut[2] = 255;
|
|
114061
|
+
bogusLut[3] = 0;
|
|
114062
|
+
bogusLut[4] = 0;
|
|
114063
|
+
bogusLut[5] = 0;
|
|
114064
|
+
bogusLut[6] = 0;
|
|
114065
|
+
bogusLut[7] = 255;
|
|
114066
|
+
for (let i2 = 0; i2 < nLayer; i2++) {
|
|
114067
|
+
let lut = bogusLut.slice();
|
|
114068
|
+
if (this.volumes[i2].colorMapNegative.length > 0)
|
|
114069
|
+
lut = this.colormap(this.volumes[i2].colorMapNegative);
|
|
114070
|
+
addColormap(lut);
|
|
114071
|
+
}
|
|
114072
|
+
this.gl.texSubImage2D(this.gl.TEXTURE_2D, 0, 0, 0, 256, nLayer * 2, this.gl.RGBA, this.gl.UNSIGNED_BYTE, luts2);
|
|
112670
114073
|
return this;
|
|
112671
114074
|
};
|
|
112672
114075
|
Niivue.prototype.sliceScale = function() {
|
|
112673
|
-
|
|
112674
|
-
|
|
112675
|
-
this.back.dims[1] * this.back.pixDims[1],
|
|
112676
|
-
this.back.dims[2] * this.back.pixDims[2],
|
|
112677
|
-
this.back.dims[3] * this.back.pixDims[3]
|
|
112678
|
-
];
|
|
112679
|
-
var longestAxis = Math.max(dims[1], Math.max(dims[2], dims[3]));
|
|
114076
|
+
let dimsMM = this.screenFieldOfViewMM(this.sliceTypeAxial);
|
|
114077
|
+
var longestAxis = Math.max(dimsMM[0], Math.max(dimsMM[1], dimsMM[2]));
|
|
112680
114078
|
var volScale = [
|
|
112681
|
-
|
|
112682
|
-
|
|
112683
|
-
|
|
114079
|
+
dimsMM[0] / longestAxis,
|
|
114080
|
+
dimsMM[1] / longestAxis,
|
|
114081
|
+
dimsMM[2] / longestAxis
|
|
112684
114082
|
];
|
|
112685
114083
|
var vox = [this.back.dims[1], this.back.dims[2], this.back.dims[3]];
|
|
112686
|
-
return { volScale, vox };
|
|
114084
|
+
return { volScale, vox, longestAxis, dimsMM };
|
|
114085
|
+
};
|
|
114086
|
+
function swizzleVec3(vec, order = [0, 1, 2]) {
|
|
114087
|
+
let vout = create$1();
|
|
114088
|
+
vout[0] = vec[order[0]];
|
|
114089
|
+
vout[1] = vec[order[1]];
|
|
114090
|
+
vout[2] = vec[order[2]];
|
|
114091
|
+
return vout;
|
|
114092
|
+
}
|
|
114093
|
+
Niivue.prototype.inRenderTile = function(x2, y) {
|
|
114094
|
+
for (let i2 = 0; i2 < this.screenSlices.length; i2++) {
|
|
114095
|
+
if (this.screenSlices[i2].axCorSag !== this.sliceTypeRender)
|
|
114096
|
+
continue;
|
|
114097
|
+
let ltwh = this.screenSlices[i2].leftTopWidthHeight;
|
|
114098
|
+
if (x2 > ltwh[0] && y > ltwh[1] && x2 < ltwh[0] + ltwh[2] && y < ltwh[1] + ltwh[3])
|
|
114099
|
+
return i2;
|
|
114100
|
+
}
|
|
114101
|
+
return -1;
|
|
114102
|
+
};
|
|
114103
|
+
Niivue.prototype.sliceScroll3D = function(posChange = 0) {
|
|
114104
|
+
if (posChange === 0)
|
|
114105
|
+
return;
|
|
114106
|
+
if (this.volumes.length > 0 && this.scene.clipPlaneDepthAziElev[0] < 1.8) {
|
|
114107
|
+
let depthAziElev = this.scene.clipPlaneDepthAziElev.slice();
|
|
114108
|
+
if (posChange > 0)
|
|
114109
|
+
depthAziElev[0] = Math.min(1.5, depthAziElev[0] + 0.025);
|
|
114110
|
+
if (posChange < 0)
|
|
114111
|
+
depthAziElev[0] = Math.max(-1.5, depthAziElev[0] - 0.025);
|
|
114112
|
+
if (depthAziElev[0] !== this.scene.clipPlaneDepthAziElev[0]) {
|
|
114113
|
+
this.scene.clipPlaneDepthAziElev = depthAziElev;
|
|
114114
|
+
return this.setClipPlane(this.scene.clipPlaneDepthAziElev);
|
|
114115
|
+
}
|
|
114116
|
+
return;
|
|
114117
|
+
}
|
|
114118
|
+
if (posChange > 0)
|
|
114119
|
+
this.volScaleMultiplier = Math.min(2, this.volScaleMultiplier * 1.1);
|
|
114120
|
+
if (posChange < 0)
|
|
114121
|
+
this.volScaleMultiplier = Math.max(0.5, this.volScaleMultiplier * 0.9);
|
|
114122
|
+
this.drawScene();
|
|
112687
114123
|
};
|
|
112688
114124
|
Niivue.prototype.mouseClick = function(x2, y, posChange = 0, isDelta = true) {
|
|
112689
114125
|
var posNow;
|
|
@@ -112706,48 +114142,23 @@ Niivue.prototype.mouseClick = function(x2, y, posChange = 0, isDelta = true) {
|
|
|
112706
114142
|
return;
|
|
112707
114143
|
}
|
|
112708
114144
|
}
|
|
112709
|
-
if (this.
|
|
112710
|
-
|
|
112711
|
-
return;
|
|
112712
|
-
if (this.volumes.length > 0 && this.scene.clipPlaneDepthAziElev[0] < 1.8) {
|
|
112713
|
-
let depthAziElev = this.scene.clipPlaneDepthAziElev.slice();
|
|
112714
|
-
if (posChange > 0)
|
|
112715
|
-
depthAziElev[0] = Math.min(1.5, depthAziElev[0] + 0.025);
|
|
112716
|
-
if (posChange < 0)
|
|
112717
|
-
depthAziElev[0] = Math.max(-1.5, depthAziElev[0] - 0.025);
|
|
112718
|
-
if (depthAziElev[0] !== this.scene.clipPlaneDepthAziElev[0]) {
|
|
112719
|
-
this.scene.clipPlaneDepthAziElev = depthAziElev;
|
|
112720
|
-
return this.setClipPlane(this.scene.clipPlaneDepthAziElev);
|
|
112721
|
-
}
|
|
112722
|
-
return;
|
|
112723
|
-
}
|
|
112724
|
-
if (posChange > 0)
|
|
112725
|
-
this.volScaleMultiplier = Math.min(2, this.volScaleMultiplier * 1.1);
|
|
112726
|
-
if (posChange < 0)
|
|
112727
|
-
this.volScaleMultiplier = Math.max(0.5, this.volScaleMultiplier * 0.9);
|
|
114145
|
+
if (this.inRenderTile(x2, y) >= 0) {
|
|
114146
|
+
this.sliceScroll3D(posChange);
|
|
112728
114147
|
this.drawScene();
|
|
112729
114148
|
return;
|
|
112730
114149
|
}
|
|
112731
|
-
if (this.
|
|
114150
|
+
if (this.screenSlices.length < 1 || this.gl.canvas.height < 1 || this.gl.canvas.width < 1)
|
|
112732
114151
|
return;
|
|
112733
|
-
for (let i2 = 0; i2 < this.
|
|
114152
|
+
for (let i2 = 0; i2 < this.screenSlices.length; i2++) {
|
|
112734
114153
|
var axCorSag = this.screenSlices[i2].axCorSag;
|
|
112735
114154
|
if (this.drawPenAxCorSag >= 0 && this.drawPenAxCorSag !== axCorSag)
|
|
112736
114155
|
continue;
|
|
112737
114156
|
if (axCorSag > this.sliceTypeSagittal)
|
|
112738
114157
|
continue;
|
|
112739
|
-
|
|
112740
|
-
|
|
112741
|
-
|
|
112742
|
-
|
|
112743
|
-
ltwh[0] += ltwh[2];
|
|
112744
|
-
ltwh[2] = -ltwh[2];
|
|
112745
|
-
}
|
|
112746
|
-
var fracX = (x2 - ltwh[0]) / ltwh[2];
|
|
112747
|
-
if (isMirror)
|
|
112748
|
-
fracX = 1 - fracX;
|
|
112749
|
-
var fracY = 1 - (y - ltwh[1]) / ltwh[3];
|
|
112750
|
-
if (fracX >= 0 && fracX < 1 && fracY >= 0 && fracY < 1) {
|
|
114158
|
+
let texFrac = this.screenXY2TextureFrac(x2, y, i2);
|
|
114159
|
+
if (texFrac[0] < 0)
|
|
114160
|
+
continue;
|
|
114161
|
+
{
|
|
112751
114162
|
if (!isDelta) {
|
|
112752
114163
|
this.scene.crosshairPos[2 - axCorSag] = posChange;
|
|
112753
114164
|
this.drawScene();
|
|
@@ -112775,18 +114186,7 @@ Niivue.prototype.mouseClick = function(x2, y, posChange = 0, isDelta = true) {
|
|
|
112775
114186
|
});
|
|
112776
114187
|
return;
|
|
112777
114188
|
}
|
|
112778
|
-
|
|
112779
|
-
this.scene.crosshairPos[0] = fracX;
|
|
112780
|
-
this.scene.crosshairPos[1] = fracY;
|
|
112781
|
-
}
|
|
112782
|
-
if (axCorSag === this.sliceTypeCoronal) {
|
|
112783
|
-
this.scene.crosshairPos[0] = fracX;
|
|
112784
|
-
this.scene.crosshairPos[2] = fracY;
|
|
112785
|
-
}
|
|
112786
|
-
if (axCorSag === this.sliceTypeSagittal) {
|
|
112787
|
-
this.scene.crosshairPos[1] = fracX;
|
|
112788
|
-
this.scene.crosshairPos[2] = fracY;
|
|
112789
|
-
}
|
|
114189
|
+
this.scene.crosshairPos = texFrac.slice();
|
|
112790
114190
|
if (this.opts.drawingEnabled) {
|
|
112791
114191
|
let pt = this.frac2vox(this.scene.crosshairPos);
|
|
112792
114192
|
if (this.opts.penValue < 0 || Object.is(this.opts.penValue, -0)) {
|
|
@@ -112800,7 +114200,7 @@ Niivue.prototype.mouseClick = function(x2, y, posChange = 0, isDelta = true) {
|
|
|
112800
114200
|
} else {
|
|
112801
114201
|
if (pt[0] === this.drawPenLocation[0] && pt[1] === this.drawPenLocation[1] && pt[2] === this.drawPenLocation[2])
|
|
112802
114202
|
return;
|
|
112803
|
-
this.
|
|
114203
|
+
this.drawPenLine(pt, this.drawPenLocation, this.opts.penValue);
|
|
112804
114204
|
}
|
|
112805
114205
|
this.drawPenLocation = pt;
|
|
112806
114206
|
if (this.opts.isFilledPen)
|
|
@@ -112820,45 +114220,274 @@ Niivue.prototype.mouseClick = function(x2, y, posChange = 0, isDelta = true) {
|
|
|
112820
114220
|
})
|
|
112821
114221
|
});
|
|
112822
114222
|
return;
|
|
112823
|
-
} else {
|
|
112824
|
-
if (x2 === null && y === null) {
|
|
112825
|
-
this.scene.crosshairPos[2 - axCorSag] = posChange;
|
|
112826
|
-
this.drawScene();
|
|
112827
|
-
return;
|
|
112828
|
-
}
|
|
112829
114223
|
}
|
|
112830
114224
|
}
|
|
112831
114225
|
};
|
|
112832
|
-
Niivue.prototype.
|
|
114226
|
+
Niivue.prototype.drawRuler = function() {
|
|
114227
|
+
let fovMM = [];
|
|
114228
|
+
let ltwh = [];
|
|
114229
|
+
for (let i2 = 0; i2 < this.screenSlices.length; i2++) {
|
|
114230
|
+
if (this.screenSlices[i2].axCorSag === this.sliceTypeRender)
|
|
114231
|
+
continue;
|
|
114232
|
+
if (this.screenSlices[i2].fovMM.length > 1) {
|
|
114233
|
+
ltwh = this.screenSlices[i2].leftTopWidthHeight;
|
|
114234
|
+
fovMM = this.screenSlices[i2].fovMM;
|
|
114235
|
+
break;
|
|
114236
|
+
}
|
|
114237
|
+
}
|
|
114238
|
+
if (ltwh.length < 4)
|
|
114239
|
+
return;
|
|
114240
|
+
let frac10cm = 100 / fovMM[0];
|
|
114241
|
+
let pix10cm = frac10cm * ltwh[2];
|
|
114242
|
+
let pixLeft = ltwh[0] + 0.5 * ltwh[2] - 0.5 * pix10cm;
|
|
114243
|
+
let pixTop = ltwh[1] + ltwh[3] - 2 * this.opts.rulerWidth;
|
|
114244
|
+
let startXYendXY = [pixLeft, pixTop, pixLeft + pix10cm, pixTop];
|
|
114245
|
+
this.drawRuler10cm(startXYendXY);
|
|
114246
|
+
};
|
|
114247
|
+
Niivue.prototype.drawRuler10cm = function(startXYendXY) {
|
|
114248
|
+
this.gl.bindVertexArray(this.genericVAO);
|
|
112833
114249
|
this.lineShader.use(this.gl);
|
|
112834
|
-
this.gl.uniform4fv(this.lineShader.uniforms["lineColor"], this.opts.
|
|
114250
|
+
this.gl.uniform4fv(this.lineShader.uniforms["lineColor"], this.opts.rulerColor);
|
|
112835
114251
|
this.gl.uniform2fv(this.lineShader.uniforms["canvasWidthHeight"], [
|
|
112836
114252
|
this.gl.canvas.width,
|
|
112837
114253
|
this.gl.canvas.height
|
|
112838
114254
|
]);
|
|
112839
|
-
this.gl.
|
|
114255
|
+
this.gl.uniform1f(this.lineShader.uniforms["thickness"], this.opts.rulerWidth);
|
|
114256
|
+
this.gl.uniform4fv(this.lineShader.uniforms["startXYendXY"], startXYendXY);
|
|
114257
|
+
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
114258
|
+
let w1cm = -0.1 * (startXYendXY[0] - startXYendXY[2]);
|
|
114259
|
+
let b = startXYendXY[1];
|
|
114260
|
+
let t = b - 2 * this.opts.rulerWidth;
|
|
114261
|
+
let t2 = b - 4 * this.opts.rulerWidth;
|
|
114262
|
+
for (let i2 = 0; i2 < 11; i2++) {
|
|
114263
|
+
let l = startXYendXY[0] + i2 * w1cm;
|
|
114264
|
+
let xyxy = [l, b, l, t];
|
|
114265
|
+
if (i2 % 5 === 0)
|
|
114266
|
+
xyxy[3] = t2;
|
|
114267
|
+
this.gl.uniform4fv(this.lineShader.uniforms["startXYendXY"], xyxy);
|
|
114268
|
+
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
114269
|
+
}
|
|
114270
|
+
this.gl.bindVertexArray(this.unusedVAO);
|
|
114271
|
+
};
|
|
114272
|
+
Niivue.prototype.drawMeasurementTool = function(startXYendXY) {
|
|
114273
|
+
let gl = this.gl;
|
|
114274
|
+
gl.bindVertexArray(this.genericVAO);
|
|
114275
|
+
gl.depthFunc(gl.ALWAYS);
|
|
114276
|
+
gl.enable(gl.BLEND);
|
|
114277
|
+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
114278
|
+
this.lineShader.use(this.gl);
|
|
114279
|
+
gl.uniform4fv(this.lineShader.uniforms["lineColor"], this.opts.rulerColor);
|
|
114280
|
+
gl.uniform2fv(this.lineShader.uniforms["canvasWidthHeight"], [
|
|
114281
|
+
gl.canvas.width,
|
|
114282
|
+
gl.canvas.height
|
|
114283
|
+
]);
|
|
114284
|
+
gl.uniform1f(this.lineShader.uniforms["thickness"], this.opts.rulerWidth);
|
|
114285
|
+
gl.uniform4fv(this.lineShader.uniforms["startXYendXY"], startXYendXY);
|
|
114286
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
114287
|
+
let c2 = this.opts.rulerColor;
|
|
114288
|
+
c2[3] = 1;
|
|
114289
|
+
gl.uniform4fv(this.lineShader.uniforms["lineColor"], c2);
|
|
114290
|
+
let w = this.opts.rulerWidth;
|
|
114291
|
+
gl.uniform1f(this.lineShader.uniforms["thickness"], w * 2);
|
|
114292
|
+
let sXYeXY = [
|
|
114293
|
+
startXYendXY[0],
|
|
114294
|
+
startXYendXY[1] - w,
|
|
114295
|
+
startXYendXY[0],
|
|
114296
|
+
startXYendXY[1] + w
|
|
114297
|
+
];
|
|
114298
|
+
gl.uniform4fv(this.lineShader.uniforms["startXYendXY"], sXYeXY);
|
|
114299
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
114300
|
+
sXYeXY = [
|
|
114301
|
+
startXYendXY[2],
|
|
114302
|
+
startXYendXY[3] - w,
|
|
114303
|
+
startXYendXY[2],
|
|
114304
|
+
startXYendXY[3] + w
|
|
114305
|
+
];
|
|
114306
|
+
gl.uniform4fv(this.lineShader.uniforms["startXYendXY"], sXYeXY);
|
|
114307
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
114308
|
+
let startXY = this.canvasPos2frac([startXYendXY[0], startXYendXY[1]]);
|
|
114309
|
+
let endXY = this.canvasPos2frac([startXYendXY[2], startXYendXY[3]]);
|
|
114310
|
+
if (startXY[0] >= 0 && endXY[0] >= 0) {
|
|
114311
|
+
startXY = this.frac2mm(startXY);
|
|
114312
|
+
endXY = this.frac2mm(endXY);
|
|
114313
|
+
let v = create$1();
|
|
114314
|
+
sub(v, startXY, endXY);
|
|
114315
|
+
let lenMM = len(v);
|
|
114316
|
+
let decimals = 2;
|
|
114317
|
+
if (lenMM > 9)
|
|
114318
|
+
decimals = 1;
|
|
114319
|
+
if (lenMM > 99)
|
|
114320
|
+
decimals = 0;
|
|
114321
|
+
let stringMM = lenMM.toFixed(decimals);
|
|
114322
|
+
this.drawTextBetween(startXYendXY, stringMM);
|
|
114323
|
+
}
|
|
114324
|
+
gl.bindVertexArray(this.unusedVAO);
|
|
114325
|
+
};
|
|
114326
|
+
Niivue.prototype.drawBox = function(leftTopWidthHeight, lineColor = [1, 0, 0, 1]) {
|
|
114327
|
+
this.rectShader.use(this.gl);
|
|
114328
|
+
this.gl.enable(this.gl.BLEND);
|
|
114329
|
+
this.gl.uniform4fv(this.rectShader.uniforms["lineColor"], lineColor);
|
|
114330
|
+
this.gl.uniform2fv(this.rectShader.uniforms["canvasWidthHeight"], [
|
|
114331
|
+
this.gl.canvas.width,
|
|
114332
|
+
this.gl.canvas.height
|
|
114333
|
+
]);
|
|
114334
|
+
this.gl.uniform4f(this.rectShader.uniforms["leftTopWidthHeight"], leftTopWidthHeight[0], leftTopWidthHeight[1], leftTopWidthHeight[2], leftTopWidthHeight[3]);
|
|
112840
114335
|
this.gl.bindVertexArray(this.genericVAO);
|
|
112841
114336
|
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
112842
114337
|
this.gl.bindVertexArray(this.unusedVAO);
|
|
112843
114338
|
};
|
|
112844
|
-
Niivue.prototype.
|
|
114339
|
+
Niivue.prototype.drawSelectionBox = function(leftTopWidthHeight, lineColor = [1, 0, 0, 1]) {
|
|
114340
|
+
this.drawBox(leftTopWidthHeight, this.opts.selectionBoxColor);
|
|
114341
|
+
};
|
|
114342
|
+
function tickSpacingX(tickCount, mn, mx) {
|
|
114343
|
+
let range = Math.abs(mx - mn);
|
|
114344
|
+
if (range === 0)
|
|
114345
|
+
return [0, 0];
|
|
114346
|
+
let unroundedTickSize = range / (tickCount - 1);
|
|
114347
|
+
let x2 = Math.ceil(Math.log10(unroundedTickSize) - 1);
|
|
114348
|
+
let pow10x = Math.pow(10, x2);
|
|
114349
|
+
let spacing = Math.ceil(unroundedTickSize / pow10x) * pow10x;
|
|
114350
|
+
let ticMin = mn;
|
|
114351
|
+
if (ticMin % spacing !== 0 && range % spacing !== 0)
|
|
114352
|
+
ticMin = Math.floor((mn + spacing) / spacing) * spacing;
|
|
114353
|
+
if (mn / spacing % 1 !== 0)
|
|
114354
|
+
ticMin = Math.sign(ticMin) * Math.round(Math.abs(ticMin));
|
|
114355
|
+
return [spacing, ticMin];
|
|
114356
|
+
}
|
|
114357
|
+
function tickSpacing(mn, mx) {
|
|
114358
|
+
let range = Math.abs(mx - mn);
|
|
114359
|
+
let [spacing, ticMin] = tickSpacingX(5, mn, mx);
|
|
114360
|
+
if (range % spacing === 0)
|
|
114361
|
+
return [spacing, ticMin];
|
|
114362
|
+
[spacing, ticMin] = tickSpacingX(4, mn, mx);
|
|
114363
|
+
if (range % spacing === 0)
|
|
114364
|
+
return [spacing, ticMin];
|
|
114365
|
+
[spacing, ticMin] = tickSpacingX(5, mn, mx);
|
|
114366
|
+
return [spacing, ticMin];
|
|
114367
|
+
}
|
|
114368
|
+
Niivue.prototype.effectiveCanvasHeight = function() {
|
|
114369
|
+
return this.gl.canvas.height - this.colorbarHeight;
|
|
114370
|
+
};
|
|
114371
|
+
Niivue.prototype.reserveColorbarPanel = function() {
|
|
114372
|
+
let txtHt = Math.max(this.opts.textHeight, 0.01);
|
|
114373
|
+
txtHt = txtHt * Math.min(this.gl.canvas.height, this.gl.canvas.width);
|
|
114374
|
+
let fullHt = 3 * txtHt;
|
|
114375
|
+
let leftTopWidthHeight = [
|
|
114376
|
+
0,
|
|
114377
|
+
this.gl.canvas.height - fullHt,
|
|
114378
|
+
this.gl.canvas.width,
|
|
114379
|
+
fullHt
|
|
114380
|
+
];
|
|
114381
|
+
this.colorbarHeight = leftTopWidthHeight[3] + 1;
|
|
114382
|
+
return leftTopWidthHeight;
|
|
114383
|
+
};
|
|
114384
|
+
Niivue.prototype.drawColorbarCore = function(layer = 0, leftTopWidthHeight = [0, 0, 0, 0], isNegativeColor = false) {
|
|
114385
|
+
if (this.volumes.length < 1)
|
|
114386
|
+
return;
|
|
114387
|
+
if (layer >= this.volumes.length)
|
|
114388
|
+
layer = 0;
|
|
112845
114389
|
if (leftTopWidthHeight[2] <= 0 || leftTopWidthHeight[3] <= 0)
|
|
112846
114390
|
return;
|
|
114391
|
+
let txtHt = Math.max(this.opts.textHeight, 0.01);
|
|
114392
|
+
txtHt = txtHt * Math.min(this.gl.canvas.height, this.gl.canvas.width);
|
|
114393
|
+
let margin = txtHt;
|
|
114394
|
+
let fullHt = 3 * txtHt;
|
|
114395
|
+
let barHt = txtHt;
|
|
114396
|
+
if (leftTopWidthHeight[3] < fullHt) {
|
|
114397
|
+
if (leftTopWidthHeight[3] < 3)
|
|
114398
|
+
return;
|
|
114399
|
+
margin = 1;
|
|
114400
|
+
barHt = leftTopWidthHeight[3] - 2;
|
|
114401
|
+
}
|
|
114402
|
+
this.gl.disable(this.gl.DEPTH_TEST);
|
|
114403
|
+
this.colorbarHeight = leftTopWidthHeight[3] + 1;
|
|
114404
|
+
[
|
|
114405
|
+
this.opts.backColor[0],
|
|
114406
|
+
this.opts.backColor[1],
|
|
114407
|
+
this.opts.backColor[2],
|
|
114408
|
+
0.5
|
|
114409
|
+
];
|
|
114410
|
+
leftTopWidthHeight[1];
|
|
114411
|
+
let barLTWH = [
|
|
114412
|
+
leftTopWidthHeight[0] + margin,
|
|
114413
|
+
leftTopWidthHeight[1],
|
|
114414
|
+
leftTopWidthHeight[2] - 2 * margin,
|
|
114415
|
+
barHt
|
|
114416
|
+
];
|
|
114417
|
+
let rimLTWH = [
|
|
114418
|
+
barLTWH[0] - 1,
|
|
114419
|
+
barLTWH[1] - 1,
|
|
114420
|
+
barLTWH[2] + 2,
|
|
114421
|
+
barLTWH[3] + 2
|
|
114422
|
+
];
|
|
114423
|
+
this.drawBox(rimLTWH, this.opts.crosshairColor);
|
|
112847
114424
|
this.colorbarShader.use(this.gl);
|
|
112848
114425
|
this.gl.activeTexture(this.gl.TEXTURE1);
|
|
112849
114426
|
this.gl.bindTexture(this.gl.TEXTURE_2D, this.colormapTexture);
|
|
112850
114427
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST);
|
|
112851
114428
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST);
|
|
114429
|
+
let lx = layer;
|
|
114430
|
+
if (isNegativeColor)
|
|
114431
|
+
lx += this.volumes.length;
|
|
114432
|
+
this.gl.uniform1f(this.colorbarShader.uniforms["layer"], lx);
|
|
112852
114433
|
this.gl.uniform2fv(this.colorbarShader.uniforms["canvasWidthHeight"], [
|
|
112853
114434
|
this.gl.canvas.width,
|
|
112854
114435
|
this.gl.canvas.height
|
|
112855
114436
|
]);
|
|
112856
|
-
this.gl.
|
|
114437
|
+
this.gl.uniform4fv(this.colorbarShader.uniforms["leftTopWidthHeight"], barLTWH);
|
|
112857
114438
|
this.gl.bindVertexArray(this.genericVAO);
|
|
112858
114439
|
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
112859
114440
|
this.gl.bindVertexArray(this.unusedVAO);
|
|
112860
114441
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
|
|
112861
114442
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
|
|
114443
|
+
let min2 = this.volumes[layer].cal_min;
|
|
114444
|
+
let max2 = this.volumes[layer].cal_max;
|
|
114445
|
+
if (min2 >= max2 || txtHt < 1)
|
|
114446
|
+
return;
|
|
114447
|
+
let range = max2 - min2;
|
|
114448
|
+
let [spacing, ticMin] = tickSpacing(min2, max2);
|
|
114449
|
+
function humanize(x2) {
|
|
114450
|
+
return x2.toFixed(6).replace(/\.?0*$/, "");
|
|
114451
|
+
}
|
|
114452
|
+
let tic = ticMin;
|
|
114453
|
+
let ticLTWH = [0, barLTWH[1] + barLTWH[3] - txtHt * 0.5, 2, txtHt * 0.75];
|
|
114454
|
+
let txtTop = ticLTWH[1] + ticLTWH[3];
|
|
114455
|
+
while (tic <= max2) {
|
|
114456
|
+
ticLTWH[0] = barLTWH[0] + (tic - min2) / range * barLTWH[2];
|
|
114457
|
+
this.drawBox(ticLTWH);
|
|
114458
|
+
let str = humanize(tic);
|
|
114459
|
+
if (isNegativeColor)
|
|
114460
|
+
str = "-" + str;
|
|
114461
|
+
this.drawTextBelow([ticLTWH[0], txtTop], str);
|
|
114462
|
+
tic += spacing;
|
|
114463
|
+
}
|
|
114464
|
+
};
|
|
114465
|
+
Niivue.prototype.drawColorbar = function() {
|
|
114466
|
+
let nlayers = this.volumes.length;
|
|
114467
|
+
if (nlayers < 1)
|
|
114468
|
+
return;
|
|
114469
|
+
let ncolorMapNegative = 0;
|
|
114470
|
+
for (let i2 = 0; i2 < nlayers; i2++)
|
|
114471
|
+
if (this.volumes[i2].colorMapNegative.length > 0)
|
|
114472
|
+
ncolorMapNegative++;
|
|
114473
|
+
let leftTopWidthHeight = this.reserveColorbarPanel();
|
|
114474
|
+
let txtHt = Math.max(this.opts.textHeight, 0.01);
|
|
114475
|
+
txtHt = txtHt * Math.min(this.gl.canvas.height, this.gl.canvas.width);
|
|
114476
|
+
let fullHt = 3 * txtHt;
|
|
114477
|
+
let wid = leftTopWidthHeight[2] / (nlayers + ncolorMapNegative);
|
|
114478
|
+
if (leftTopWidthHeight[2] <= 0 || leftTopWidthHeight[3] <= 0) {
|
|
114479
|
+
wid = this.gl.canvas.width / nlayers;
|
|
114480
|
+
leftTopWidthHeight = [0, this.gl.canvas.height - fullHt, wid, fullHt];
|
|
114481
|
+
}
|
|
114482
|
+
leftTopWidthHeight[2] = wid;
|
|
114483
|
+
for (let i2 = 0; i2 < nlayers; i2++) {
|
|
114484
|
+
this.drawColorbarCore(i2, leftTopWidthHeight);
|
|
114485
|
+
leftTopWidthHeight[0] += wid;
|
|
114486
|
+
if (this.volumes[i2].colorMapNegative.length === 0)
|
|
114487
|
+
continue;
|
|
114488
|
+
this.drawColorbarCore(i2, leftTopWidthHeight, true);
|
|
114489
|
+
leftTopWidthHeight[0] += wid;
|
|
114490
|
+
}
|
|
112862
114491
|
};
|
|
112863
114492
|
Niivue.prototype.textWidth = function(scale2, str) {
|
|
112864
114493
|
let w = 0;
|
|
@@ -112919,6 +114548,34 @@ Niivue.prototype.drawTextLeft = function(xy, str, scale2 = 1, color = null) {
|
|
|
112919
114548
|
xy[1] -= 0.5 * size;
|
|
112920
114549
|
this.drawText(xy, str, scale2, color);
|
|
112921
114550
|
};
|
|
114551
|
+
Niivue.prototype.drawTextRightBelow = function(xy, str, scale2 = 1, color = null) {
|
|
114552
|
+
if (this.opts.textHeight <= 0)
|
|
114553
|
+
return;
|
|
114554
|
+
this.opts.textHeight * this.gl.canvas.height * scale2;
|
|
114555
|
+
this.drawText(xy, str, scale2, color);
|
|
114556
|
+
};
|
|
114557
|
+
Niivue.prototype.drawTextBetween = function(startXYendXY, str, scale2 = 1, color = null) {
|
|
114558
|
+
if (this.opts.textHeight <= 0)
|
|
114559
|
+
return;
|
|
114560
|
+
let xy = [
|
|
114561
|
+
(startXYendXY[0] + startXYendXY[2]) * 0.5,
|
|
114562
|
+
(startXYendXY[1] + startXYendXY[3]) * 0.5
|
|
114563
|
+
];
|
|
114564
|
+
let size = this.opts.textHeight * this.gl.canvas.height * scale2;
|
|
114565
|
+
let w = this.textWidth(size, str);
|
|
114566
|
+
xy[0] -= 0.5 * w;
|
|
114567
|
+
xy[1] -= 0.5 * size;
|
|
114568
|
+
let LTWH = [xy[0] - 1, xy[1] - 1, w + 2, size + 2];
|
|
114569
|
+
let clr = color;
|
|
114570
|
+
if (clr === null)
|
|
114571
|
+
clr = this.opts.crosshairColor;
|
|
114572
|
+
if (clr[0] + clr[1] + clr[2] > 0.8)
|
|
114573
|
+
clr = [0, 0, 0, 0.5];
|
|
114574
|
+
else
|
|
114575
|
+
clr = [1, 1, 1, 0.5];
|
|
114576
|
+
this.drawRect(LTWH, clr);
|
|
114577
|
+
this.drawText(xy, str, scale2, color);
|
|
114578
|
+
};
|
|
112922
114579
|
Niivue.prototype.drawTextBelow = function(xy, str, scale2 = 1, color = null) {
|
|
112923
114580
|
if (this.opts.textHeight <= 0)
|
|
112924
114581
|
return;
|
|
@@ -112952,117 +114609,271 @@ Niivue.prototype.setInterpolation = function(isNearest) {
|
|
|
112952
114609
|
this.updateInterpolation(1);
|
|
112953
114610
|
this.drawScene();
|
|
112954
114611
|
};
|
|
112955
|
-
|
|
114612
|
+
function deg2rad(deg) {
|
|
114613
|
+
return deg * (Math.PI / 180);
|
|
114614
|
+
}
|
|
114615
|
+
Niivue.prototype.calculateMvpMatrix2D = function(leftTopWidthHeight, mn, mx, clipTolerance = Infinity, clipDepth = 0, azimuth = null, elevation = null, isRadiolgical) {
|
|
112956
114616
|
let gl = this.gl;
|
|
112957
|
-
gl.viewport(leftTopWidthHeight[0], leftTopWidthHeight[1], leftTopWidthHeight[2], leftTopWidthHeight[3]);
|
|
112958
|
-
let
|
|
112959
|
-
|
|
112960
|
-
let
|
|
112961
|
-
let
|
|
114617
|
+
gl.viewport(leftTopWidthHeight[0], this.gl.canvas.clientHeight - (leftTopWidthHeight[1] + leftTopWidthHeight[3]), leftTopWidthHeight[2], leftTopWidthHeight[3]);
|
|
114618
|
+
let left = mn[0];
|
|
114619
|
+
let right = mx[0];
|
|
114620
|
+
let leftTopMM = [left, mn[1]];
|
|
114621
|
+
let fovMM = [right - left, mx[1] - mn[1]];
|
|
114622
|
+
if (isRadiolgical) {
|
|
114623
|
+
leftTopMM = [mx[0], mn[1]];
|
|
114624
|
+
fovMM = [mn[0] - mx[0], mx[1] - mn[1]];
|
|
114625
|
+
left = -mx[0];
|
|
114626
|
+
right = -mn[0];
|
|
114627
|
+
}
|
|
114628
|
+
let scale2 = 2 * Math.max(Math.abs(mn[2]), Math.abs(mx[2]));
|
|
112962
114629
|
let projectionMatrix = create$2();
|
|
112963
|
-
|
|
112964
|
-
|
|
112965
|
-
|
|
112966
|
-
|
|
114630
|
+
let near = 0.01;
|
|
114631
|
+
let far = scale2 * 8;
|
|
114632
|
+
if (clipTolerance !== Infinity) {
|
|
114633
|
+
let r = isRadiolgical;
|
|
114634
|
+
if (elevation === 0 && (azimuth === 0 || azimuth === 180))
|
|
114635
|
+
r = !r;
|
|
114636
|
+
let dx = scale2 * 1.8 - clipDepth;
|
|
114637
|
+
if (!r)
|
|
114638
|
+
dx = scale2 * 1.8 + clipDepth;
|
|
114639
|
+
near = dx - clipTolerance;
|
|
114640
|
+
far = dx + clipTolerance;
|
|
114641
|
+
}
|
|
114642
|
+
ortho(projectionMatrix, left, right, mn[1], mx[1], near, far);
|
|
112967
114643
|
const modelMatrix = create$2();
|
|
112968
114644
|
modelMatrix[0] = -1;
|
|
112969
114645
|
let translateVec3 = fromValues$1(0, 0, -scale2 * 1.8);
|
|
112970
114646
|
translate(modelMatrix, modelMatrix, translateVec3);
|
|
112971
|
-
|
|
112972
|
-
|
|
112973
|
-
translate(modelMatrix, modelMatrix, [
|
|
112974
|
-
-origin[0],
|
|
112975
|
-
-origin[1],
|
|
112976
|
-
-origin[2]
|
|
112977
|
-
]);
|
|
114647
|
+
rotateX(modelMatrix, modelMatrix, deg2rad(270 - elevation));
|
|
114648
|
+
rotateZ(modelMatrix, modelMatrix, deg2rad(azimuth - 180));
|
|
112978
114649
|
let iModelMatrix = create$2();
|
|
112979
114650
|
invert(iModelMatrix, modelMatrix);
|
|
112980
114651
|
let normalMatrix = create$2();
|
|
112981
114652
|
transpose(normalMatrix, iModelMatrix);
|
|
112982
114653
|
let modelViewProjectionMatrix = create$2();
|
|
112983
|
-
multiply
|
|
112984
|
-
return
|
|
114654
|
+
multiply(modelViewProjectionMatrix, projectionMatrix, modelMatrix);
|
|
114655
|
+
return {
|
|
114656
|
+
modelViewProjectionMatrix,
|
|
114657
|
+
modelMatrix,
|
|
114658
|
+
normalMatrix,
|
|
114659
|
+
leftTopMM,
|
|
114660
|
+
fovMM
|
|
114661
|
+
};
|
|
112985
114662
|
};
|
|
112986
|
-
Niivue.prototype.
|
|
112987
|
-
if (this.
|
|
114663
|
+
Niivue.prototype.swizzleVec3MM = function(v3, axCorSag) {
|
|
114664
|
+
if (axCorSag === this.sliceTypeCoronal) {
|
|
114665
|
+
v3 = swizzleVec3(v3, [0, 2, 1]);
|
|
114666
|
+
} else if (axCorSag === this.sliceTypeSagittal) {
|
|
114667
|
+
v3 = swizzleVec3(v3, [1, 2, 0]);
|
|
114668
|
+
}
|
|
114669
|
+
return v3;
|
|
114670
|
+
};
|
|
114671
|
+
Niivue.prototype.screenFieldOfViewMM = function(axCorSag = 0, forceSliceMM = false) {
|
|
114672
|
+
if (!forceSliceMM && !this.opts.isSliceMM) {
|
|
114673
|
+
let fov = this.volumeObject3D.fieldOfViewDeObliqueMM.slice();
|
|
114674
|
+
return this.swizzleVec3MM(fov, axCorSag);
|
|
114675
|
+
}
|
|
114676
|
+
let mnMM = this.volumeObject3D.extentsMin.slice();
|
|
114677
|
+
let mxMM = this.volumeObject3D.extentsMax.slice();
|
|
114678
|
+
create$2();
|
|
114679
|
+
mnMM = this.swizzleVec3MM(mnMM, axCorSag);
|
|
114680
|
+
mxMM = this.swizzleVec3MM(mxMM, axCorSag);
|
|
114681
|
+
let fovMM = create$1();
|
|
114682
|
+
subtract(fovMM, mxMM, mnMM);
|
|
114683
|
+
return fovMM;
|
|
114684
|
+
};
|
|
114685
|
+
Niivue.prototype.screenFieldOfViewExtendedMM = function(axCorSag = 0) {
|
|
114686
|
+
let mnMM = this.volumeObject3D.extentsMin.slice();
|
|
114687
|
+
let mxMM = this.volumeObject3D.extentsMax.slice();
|
|
114688
|
+
let rotation = create$2();
|
|
114689
|
+
mnMM = this.swizzleVec3MM(mnMM, axCorSag);
|
|
114690
|
+
mxMM = this.swizzleVec3MM(mxMM, axCorSag);
|
|
114691
|
+
let fovMM = create$1();
|
|
114692
|
+
subtract(fovMM, mxMM, mnMM);
|
|
114693
|
+
return { mnMM, mxMM, rotation, fovMM };
|
|
114694
|
+
};
|
|
114695
|
+
Niivue.prototype.drawSliceOrientationText = function(leftTopWidthHeight, axCorSag) {
|
|
114696
|
+
this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
|
|
114697
|
+
let topText = "S";
|
|
114698
|
+
if (axCorSag === this.sliceTypeAxial)
|
|
114699
|
+
topText = "A";
|
|
114700
|
+
let leftText = this.opts.isRadiologicalConvention ? "R" : "L";
|
|
114701
|
+
if (axCorSag === this.sliceTypeSagittal)
|
|
114702
|
+
leftText = this.opts.sagittalNoseLeft ? "A" : "P";
|
|
114703
|
+
if (this.opts.isCornerOrientationText) {
|
|
114704
|
+
this.drawTextRightBelow([leftTopWidthHeight[0], leftTopWidthHeight[1]], leftText + topText);
|
|
112988
114705
|
return;
|
|
114706
|
+
}
|
|
114707
|
+
this.drawTextBelow([
|
|
114708
|
+
leftTopWidthHeight[0] + leftTopWidthHeight[2] * 0.5,
|
|
114709
|
+
leftTopWidthHeight[1]
|
|
114710
|
+
], topText);
|
|
114711
|
+
this.drawTextRight([
|
|
114712
|
+
leftTopWidthHeight[0],
|
|
114713
|
+
leftTopWidthHeight[1] + leftTopWidthHeight[3] * 0.5
|
|
114714
|
+
], leftText);
|
|
114715
|
+
};
|
|
114716
|
+
Niivue.prototype.xyMM2xyzMM = function(axCorSag, sliceFrac) {
|
|
114717
|
+
let sliceDim = 2;
|
|
114718
|
+
if (axCorSag === this.sliceTypeCoronal)
|
|
114719
|
+
sliceDim = 1;
|
|
114720
|
+
if (axCorSag === this.sliceTypeSagittal)
|
|
114721
|
+
sliceDim = 0;
|
|
114722
|
+
let a = [0, 0, 0];
|
|
114723
|
+
let b = [1, 1, 0];
|
|
114724
|
+
let c2 = [1, 0, 1];
|
|
114725
|
+
a[sliceDim] = sliceFrac;
|
|
114726
|
+
b[sliceDim] = sliceFrac;
|
|
114727
|
+
c2[sliceDim] = sliceFrac;
|
|
114728
|
+
a = this.frac2mm(a);
|
|
114729
|
+
b = this.frac2mm(b);
|
|
114730
|
+
c2 = this.frac2mm(c2);
|
|
114731
|
+
a = this.swizzleVec3MM(a, axCorSag);
|
|
114732
|
+
b = this.swizzleVec3MM(b, axCorSag);
|
|
114733
|
+
c2 = this.swizzleVec3MM(c2, axCorSag);
|
|
114734
|
+
let denom = (b[0] - a[0]) * (c2[1] - a[1]) - (c2[0] - a[0]) * (b[1] - a[1]);
|
|
114735
|
+
let yMult = (b[0] - a[0]) * (c2[2] - a[2]) - (c2[0] - a[0]) * (b[2] - a[2]);
|
|
114736
|
+
yMult /= denom;
|
|
114737
|
+
let xMult = (b[1] - a[1]) * (c2[2] - a[2]) - (c2[1] - a[1]) * (b[2] - a[2]);
|
|
114738
|
+
xMult /= denom;
|
|
114739
|
+
let AxyzMxy = [0, 0, 0, 0, 0];
|
|
114740
|
+
AxyzMxy[0] = a[0];
|
|
114741
|
+
AxyzMxy[1] = a[1];
|
|
114742
|
+
AxyzMxy[2] = a[2];
|
|
114743
|
+
AxyzMxy[3] = xMult;
|
|
114744
|
+
AxyzMxy[4] = yMult;
|
|
114745
|
+
return AxyzMxy;
|
|
114746
|
+
};
|
|
114747
|
+
Niivue.prototype.draw2DMM = function(leftTopWidthHeight, axCorSag, customMM = NaN) {
|
|
114748
|
+
let screen2 = this.screenFieldOfViewExtendedMM(axCorSag);
|
|
114749
|
+
let isRadiolgical = this.opts.isRadiologicalConvention && axCorSag < this.sliceTypeSagittal;
|
|
114750
|
+
if (customMM === Infinity || customMM === -Infinity) {
|
|
114751
|
+
isRadiolgical = customMM !== Infinity;
|
|
114752
|
+
if (axCorSag === this.sliceTypeCoronal)
|
|
114753
|
+
isRadiolgical = !isRadiolgical;
|
|
114754
|
+
} else if (this.opts.sagittalNoseLeft && axCorSag === this.sliceTypeSagittal)
|
|
114755
|
+
isRadiolgical = !isRadiolgical;
|
|
114756
|
+
let elevation = 0;
|
|
114757
|
+
let azimuth = 0;
|
|
114758
|
+
if (axCorSag === this.sliceTypeSagittal) {
|
|
114759
|
+
azimuth = isRadiolgical ? 90 : -90;
|
|
114760
|
+
} else if (axCorSag === this.sliceTypeCoronal) {
|
|
114761
|
+
azimuth = isRadiolgical ? 180 : 0;
|
|
114762
|
+
} else {
|
|
114763
|
+
azimuth = isRadiolgical ? 180 : 0;
|
|
114764
|
+
elevation = isRadiolgical ? -90 : 90;
|
|
114765
|
+
}
|
|
114766
|
+
if (leftTopWidthHeight[2] === 0 || leftTopWidthHeight[3] === 0)
|
|
114767
|
+
leftTopWidthHeight = this.scaleSlice(screen2.fovMM[0], screen2.fovMM[1]);
|
|
114768
|
+
if (isNaN(customMM)) {
|
|
114769
|
+
let panXY = this.swizzleVec3MM(this.scene.pan2Dxyzmm, axCorSag);
|
|
114770
|
+
screen2.mnMM[0] -= panXY[0];
|
|
114771
|
+
screen2.mxMM[0] -= panXY[0];
|
|
114772
|
+
screen2.mnMM[1] -= panXY[1];
|
|
114773
|
+
screen2.mxMM[1] -= panXY[1];
|
|
114774
|
+
screen2.mnMM[0] /= this.scene.pan2Dxyzmm[3];
|
|
114775
|
+
screen2.mxMM[0] /= this.scene.pan2Dxyzmm[3];
|
|
114776
|
+
screen2.mnMM[1] /= this.scene.pan2Dxyzmm[3];
|
|
114777
|
+
screen2.mxMM[1] /= this.scene.pan2Dxyzmm[3];
|
|
114778
|
+
}
|
|
114779
|
+
let sliceDim = 2;
|
|
114780
|
+
if (axCorSag === this.sliceTypeCoronal)
|
|
114781
|
+
sliceDim = 1;
|
|
114782
|
+
if (axCorSag === this.sliceTypeSagittal)
|
|
114783
|
+
sliceDim = 0;
|
|
114784
|
+
let sliceFrac = this.scene.crosshairPos[sliceDim];
|
|
114785
|
+
let mm = this.frac2mm(this.scene.crosshairPos);
|
|
114786
|
+
if (!isNaN(customMM) && customMM !== Infinity && customMM !== -Infinity) {
|
|
114787
|
+
mm = this.frac2mm([0.5, 0.5, 0.5]);
|
|
114788
|
+
mm[sliceDim] = customMM;
|
|
114789
|
+
let frac = this.mm2frac(mm);
|
|
114790
|
+
sliceFrac = frac[sliceDim];
|
|
114791
|
+
}
|
|
114792
|
+
let sliceMM = mm[sliceDim];
|
|
112989
114793
|
let gl = this.gl;
|
|
112990
|
-
|
|
112991
|
-
|
|
114794
|
+
gl.clear(gl.DEPTH_BUFFER_BIT);
|
|
114795
|
+
let obj = this.calculateMvpMatrix2D(leftTopWidthHeight, screen2.mnMM, screen2.mxMM, Infinity, 0, azimuth, elevation, isRadiolgical);
|
|
114796
|
+
if (customMM === Infinity || customMM === -Infinity) {
|
|
114797
|
+
this.draw3D(leftTopWidthHeight, obj.modelViewProjectionMatrix, obj.modelMatrix, obj.normalMatrix, azimuth, elevation);
|
|
114798
|
+
return;
|
|
114799
|
+
}
|
|
112992
114800
|
gl.enable(gl.DEPTH_TEST);
|
|
112993
114801
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
112994
114802
|
gl.disable(gl.BLEND);
|
|
112995
114803
|
gl.depthFunc(gl.GREATER);
|
|
112996
|
-
if (isDepthTest) {
|
|
112997
|
-
gl.disable(gl.BLEND);
|
|
112998
|
-
gl.depthFunc(gl.GREATER);
|
|
112999
|
-
} else {
|
|
113000
|
-
gl.enable(gl.BLEND);
|
|
113001
|
-
gl.depthFunc(gl.ALWAYS);
|
|
113002
|
-
}
|
|
113003
114804
|
gl.disable(gl.CULL_FACE);
|
|
113004
|
-
this.
|
|
113005
|
-
gl.
|
|
113006
|
-
gl.
|
|
113007
|
-
gl.
|
|
113008
|
-
gl.uniform1f(this.
|
|
113009
|
-
|
|
113010
|
-
|
|
113011
|
-
|
|
113012
|
-
|
|
113013
|
-
|
|
113014
|
-
|
|
113015
|
-
|
|
113016
|
-
|
|
113017
|
-
|
|
113018
|
-
|
|
113019
|
-
|
|
113020
|
-
|
|
113021
|
-
|
|
113022
|
-
|
|
113023
|
-
|
|
113024
|
-
return;
|
|
114805
|
+
this.sliceMMShader.use(this.gl);
|
|
114806
|
+
this.gl.uniform1i(this.sliceMMShader.uniforms["backgroundMasksOverlays"], this.backgroundMasksOverlays);
|
|
114807
|
+
this.gl.uniform1f(this.sliceMMShader.uniforms["opacity"], this.volumes[0].opacity);
|
|
114808
|
+
this.gl.uniform1i(this.sliceMMShader.uniforms["axCorSag"], axCorSag);
|
|
114809
|
+
this.gl.uniform1f(this.sliceMMShader.uniforms["slice"], sliceFrac);
|
|
114810
|
+
this.gl.uniformMatrix4fv(this.sliceMMShader.uniforms["frac2mm"], false, this.volumes[0].frac2mm);
|
|
114811
|
+
this.gl.uniformMatrix4fv(this.sliceMMShader.uniforms["mvpMtx"], false, obj.modelViewProjectionMatrix.slice());
|
|
114812
|
+
this.gl.bindVertexArray(this.genericVAO);
|
|
114813
|
+
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
114814
|
+
this.gl.bindVertexArray(this.unusedVAO);
|
|
114815
|
+
this.screenSlices.push({
|
|
114816
|
+
leftTopWidthHeight,
|
|
114817
|
+
axCorSag,
|
|
114818
|
+
sliceFrac,
|
|
114819
|
+
AxyzMxy: this.xyMM2xyzMM(axCorSag, sliceFrac),
|
|
114820
|
+
leftTopMM: obj.leftTopMM,
|
|
114821
|
+
fovMM: obj.fovMM
|
|
114822
|
+
});
|
|
114823
|
+
if (isNaN(customMM)) {
|
|
114824
|
+
this.drawCrosshairs3D(true, 1, obj.modelViewProjectionMatrix);
|
|
113025
114825
|
}
|
|
113026
|
-
|
|
113027
|
-
|
|
113028
|
-
|
|
113029
|
-
|
|
113030
|
-
for (let i2 = 0; i2 < this.meshes.length; i2++) {
|
|
113031
|
-
if (this.meshes[i2].indexCount < 3)
|
|
113032
|
-
continue;
|
|
113033
|
-
if (!this.meshes[i2].offsetPt0)
|
|
113034
|
-
continue;
|
|
113035
|
-
gl.bindVertexArray(this.meshes[i2].vao);
|
|
113036
|
-
gl.drawElements(gl.LINE_STRIP, this.meshes[i2].indexCount, gl.UNSIGNED_INT, 0);
|
|
113037
|
-
gl.bindVertexArray(this.unusedVAO);
|
|
114826
|
+
if (this.opts.meshThicknessOn2D > 0) {
|
|
114827
|
+
if (this.opts.meshThicknessOn2D !== Infinity)
|
|
114828
|
+
obj = this.calculateMvpMatrix2D(leftTopWidthHeight, screen2.mnMM, screen2.mxMM, this.opts.meshThicknessOn2D, sliceMM, azimuth, elevation, isRadiolgical);
|
|
114829
|
+
this.drawMesh3D(true, 1, obj.modelViewProjectionMatrix, obj.modelMatrix, obj.normalMatrix);
|
|
113038
114830
|
}
|
|
113039
|
-
|
|
113040
|
-
|
|
114831
|
+
if (isNaN(customMM))
|
|
114832
|
+
this.drawCrosshairs3D(false, 0.15, obj.modelViewProjectionMatrix);
|
|
114833
|
+
this.drawSliceOrientationText(leftTopWidthHeight, axCorSag);
|
|
113041
114834
|
this.readyForSync = true;
|
|
113042
114835
|
};
|
|
113043
|
-
Niivue.prototype.
|
|
114836
|
+
Niivue.prototype.draw2DVox = function(leftTopWidthHeight, axCorSag, customMM = NaN) {
|
|
114837
|
+
let fovMM = this.screenFieldOfViewMM(axCorSag);
|
|
114838
|
+
if (leftTopWidthHeight[2] === 0 || leftTopWidthHeight[3] === 0) {
|
|
114839
|
+
leftTopWidthHeight = this.scaleSlice(fovMM[0], fovMM[1]);
|
|
114840
|
+
}
|
|
113044
114841
|
this.gl.cullFace(this.gl.FRONT);
|
|
114842
|
+
let ltwh = leftTopWidthHeight.slice();
|
|
113045
114843
|
let gl = this.gl;
|
|
113046
114844
|
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
|
113047
114845
|
gl.disable(gl.DEPTH_TEST);
|
|
113048
|
-
|
|
114846
|
+
let sliceDim = 2;
|
|
114847
|
+
let crossXYZ = [
|
|
113049
114848
|
this.scene.crosshairPos[0],
|
|
113050
114849
|
this.scene.crosshairPos[1],
|
|
113051
114850
|
this.scene.crosshairPos[2]
|
|
113052
114851
|
];
|
|
113053
|
-
if (axCorSag === this.sliceTypeCoronal)
|
|
114852
|
+
if (axCorSag === this.sliceTypeCoronal) {
|
|
114853
|
+
sliceDim = 1;
|
|
113054
114854
|
crossXYZ = [
|
|
113055
114855
|
this.scene.crosshairPos[0],
|
|
113056
114856
|
this.scene.crosshairPos[2],
|
|
113057
114857
|
this.scene.crosshairPos[1]
|
|
113058
114858
|
];
|
|
113059
|
-
|
|
114859
|
+
}
|
|
114860
|
+
if (axCorSag === this.sliceTypeSagittal) {
|
|
114861
|
+
sliceDim = 0;
|
|
113060
114862
|
crossXYZ = [
|
|
113061
114863
|
this.scene.crosshairPos[1],
|
|
113062
114864
|
this.scene.crosshairPos[2],
|
|
113063
114865
|
this.scene.crosshairPos[0]
|
|
113064
114866
|
];
|
|
114867
|
+
}
|
|
114868
|
+
if (!isNaN(customMM)) {
|
|
114869
|
+
let mm = this.frac2mm([0.5, 0.5, 0.5]);
|
|
114870
|
+
mm[sliceDim] = customMM;
|
|
114871
|
+
let frac = this.mm2frac(mm);
|
|
114872
|
+
crossXYZ[2] = frac[sliceDim];
|
|
114873
|
+
}
|
|
113065
114874
|
let isMirrorLR = this.opts.isRadiologicalConvention && axCorSag < this.sliceTypeSagittal;
|
|
114875
|
+
if (this.opts.sagittalNoseLeft && axCorSag === this.sliceTypeSagittal)
|
|
114876
|
+
isMirrorLR = !isMirrorLR;
|
|
113066
114877
|
this.sliceShader.use(this.gl);
|
|
113067
114878
|
this.gl.uniform1f(this.sliceShader.uniforms["opacity"], this.volumes[0].opacity);
|
|
113068
114879
|
this.gl.uniform1i(this.sliceShader.uniforms["backgroundMasksOverlays"], this.backgroundMasksOverlays);
|
|
@@ -113080,58 +114891,50 @@ Niivue.prototype.draw2D = function(leftTopWidthHeight, axCorSag) {
|
|
|
113080
114891
|
this.gl.uniform4f(this.sliceShader.uniforms["leftTopWidthHeight"], leftTopWidthHeight[0], leftTopWidthHeight[1], leftTopWidthHeight[2], leftTopWidthHeight[3]);
|
|
113081
114892
|
this.gl.bindVertexArray(this.genericVAO);
|
|
113082
114893
|
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
113083
|
-
this.screenSlices
|
|
113084
|
-
|
|
113085
|
-
|
|
114894
|
+
this.screenSlices.push({
|
|
114895
|
+
leftTopWidthHeight,
|
|
114896
|
+
axCorSag,
|
|
114897
|
+
sliceFrac: crossXYZ[2],
|
|
114898
|
+
AxyzMxy: [],
|
|
114899
|
+
leftTopMM: [],
|
|
114900
|
+
fovMM: [fovMM[0], fovMM[1]]
|
|
114901
|
+
});
|
|
114902
|
+
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
|
114903
|
+
this.drawSliceOrientationText(ltwh, axCorSag);
|
|
114904
|
+
if (!isNaN(customMM))
|
|
114905
|
+
return;
|
|
113086
114906
|
if (this.opts.crosshairWidth <= 0)
|
|
113087
114907
|
return;
|
|
113088
|
-
this.
|
|
113089
|
-
this.
|
|
113090
|
-
this.gl.
|
|
114908
|
+
this.gl.bindVertexArray(this.genericVAO);
|
|
114909
|
+
this.rectShader.use(this.gl);
|
|
114910
|
+
this.gl.uniform4fv(this.rectShader.uniforms["lineColor"], this.opts.crosshairColor);
|
|
114911
|
+
this.gl.uniform2fv(this.rectShader.uniforms["canvasWidthHeight"], [
|
|
113091
114912
|
this.gl.canvas.width,
|
|
113092
114913
|
this.gl.canvas.height
|
|
113093
114914
|
]);
|
|
113094
114915
|
var xleft = leftTopWidthHeight[0] + leftTopWidthHeight[2] * crossXYZ[0];
|
|
113095
|
-
this.gl.uniform4f(this.
|
|
114916
|
+
this.gl.uniform4f(this.rectShader.uniforms["leftTopWidthHeight"], xleft - 0.5 * this.opts.crosshairWidth, leftTopWidthHeight[1], this.opts.crosshairWidth, leftTopWidthHeight[3]);
|
|
113096
114917
|
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
113097
114918
|
var xtop = leftTopWidthHeight[1] + leftTopWidthHeight[3] * (1 - crossXYZ[1]);
|
|
113098
|
-
this.gl.uniform4f(this.
|
|
114919
|
+
this.gl.uniform4f(this.rectShader.uniforms["leftTopWidthHeight"], leftTopWidthHeight[0], xtop - 0.5 * this.opts.crosshairWidth, leftTopWidthHeight[2], this.opts.crosshairWidth);
|
|
113099
114920
|
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
113100
114921
|
this.gl.bindVertexArray(this.unusedVAO);
|
|
113101
114922
|
this.gl.enable(this.gl.CULL_FACE);
|
|
113102
|
-
if (isMirrorLR)
|
|
113103
|
-
this.drawTextRight([
|
|
113104
|
-
leftTopWidthHeight[0] + leftTopWidthHeight[2] + 1,
|
|
113105
|
-
leftTopWidthHeight[1] + 0.5 * leftTopWidthHeight[3]
|
|
113106
|
-
], "R");
|
|
113107
|
-
else if (axCorSag < this.sliceTypeSagittal)
|
|
113108
|
-
this.drawTextRight([
|
|
113109
|
-
leftTopWidthHeight[0] + 1,
|
|
113110
|
-
leftTopWidthHeight[1] + 0.5 * leftTopWidthHeight[3]
|
|
113111
|
-
], "L");
|
|
113112
|
-
if (axCorSag === this.sliceTypeAxial)
|
|
113113
|
-
this.drawTextBelow([
|
|
113114
|
-
leftTopWidthHeight[0] + 0.5 * leftTopWidthHeight[2],
|
|
113115
|
-
leftTopWidthHeight[1] + 1
|
|
113116
|
-
], "A");
|
|
113117
|
-
if (axCorSag > this.sliceTypeAxial)
|
|
113118
|
-
this.drawTextBelow([
|
|
113119
|
-
leftTopWidthHeight[0] + 0.5 * leftTopWidthHeight[2],
|
|
113120
|
-
leftTopWidthHeight[1] + 1
|
|
113121
|
-
], "S");
|
|
113122
|
-
if (axCorSag !== this.sliceTypeAxial)
|
|
113123
|
-
return;
|
|
113124
|
-
gl.viewport(leftTopWidthHeight[0], leftTopWidthHeight[1], leftTopWidthHeight[2], leftTopWidthHeight[3]);
|
|
113125
114923
|
gl.enable(gl.DEPTH_TEST);
|
|
113126
114924
|
gl.depthFunc(gl.ALWAYS);
|
|
113127
114925
|
gl.clearDepth(0);
|
|
113128
114926
|
this.sync();
|
|
113129
114927
|
};
|
|
113130
|
-
Niivue.prototype.
|
|
113131
|
-
|
|
113132
|
-
|
|
113133
|
-
|
|
113134
|
-
|
|
114928
|
+
Niivue.prototype.draw2D = function(leftTopWidthHeight, axCorSag, customMM = NaN) {
|
|
114929
|
+
if (this.opts.isSliceMM)
|
|
114930
|
+
this.draw2DMM(leftTopWidthHeight, axCorSag, customMM);
|
|
114931
|
+
else
|
|
114932
|
+
this.draw2DVox(leftTopWidthHeight, axCorSag, customMM);
|
|
114933
|
+
};
|
|
114934
|
+
Niivue.prototype.calculateMvpMatrix = function(unused, leftTopWidthHeight = [0, 0, 0, 0], azimuth, elevation) {
|
|
114935
|
+
if (leftTopWidthHeight[2] === 0 || leftTopWidthHeight[3] === 0)
|
|
114936
|
+
leftTopWidthHeight = [0, 0, this.gl.canvas.width, this.gl.canvas.height];
|
|
114937
|
+
let whratio = leftTopWidthHeight[2] / leftTopWidthHeight[3];
|
|
113135
114938
|
let scale2 = this.furthestFromPivot;
|
|
113136
114939
|
let origin = this.pivot3D;
|
|
113137
114940
|
let projectionMatrix = create$2();
|
|
@@ -113146,8 +114949,8 @@ Niivue.prototype.calculateMvpMatrix = function() {
|
|
|
113146
114949
|
translate(modelMatrix, modelMatrix, translateVec3);
|
|
113147
114950
|
if (this.position)
|
|
113148
114951
|
translate(modelMatrix, modelMatrix, this.position);
|
|
113149
|
-
rotateX(modelMatrix, modelMatrix, deg2rad(270 -
|
|
113150
|
-
rotateZ(modelMatrix, modelMatrix, deg2rad(
|
|
114952
|
+
rotateX(modelMatrix, modelMatrix, deg2rad(270 - elevation));
|
|
114953
|
+
rotateZ(modelMatrix, modelMatrix, deg2rad(azimuth - 180));
|
|
113151
114954
|
translate(modelMatrix, modelMatrix, [
|
|
113152
114955
|
-origin[0],
|
|
113153
114956
|
-origin[1],
|
|
@@ -113158,24 +114961,24 @@ Niivue.prototype.calculateMvpMatrix = function() {
|
|
|
113158
114961
|
let normalMatrix = create$2();
|
|
113159
114962
|
transpose(normalMatrix, iModelMatrix);
|
|
113160
114963
|
let modelViewProjectionMatrix = create$2();
|
|
113161
|
-
multiply
|
|
114964
|
+
multiply(modelViewProjectionMatrix, projectionMatrix, modelMatrix);
|
|
113162
114965
|
return [modelViewProjectionMatrix, modelMatrix, normalMatrix];
|
|
113163
114966
|
};
|
|
113164
|
-
Niivue.prototype.calculateRayDirection = function() {
|
|
113165
|
-
function
|
|
114967
|
+
Niivue.prototype.calculateRayDirection = function(azimuth, elevation) {
|
|
114968
|
+
function deg2rad2(deg) {
|
|
113166
114969
|
return deg * (Math.PI / 180);
|
|
113167
114970
|
}
|
|
113168
114971
|
const modelMatrix = create$2();
|
|
113169
114972
|
modelMatrix[0] = -1;
|
|
113170
|
-
rotateX(modelMatrix, modelMatrix,
|
|
113171
|
-
rotateZ(modelMatrix, modelMatrix,
|
|
114973
|
+
rotateX(modelMatrix, modelMatrix, deg2rad2(270 - elevation));
|
|
114974
|
+
rotateZ(modelMatrix, modelMatrix, deg2rad2(azimuth - 180));
|
|
113172
114975
|
if (this.back.obliqueRAS) {
|
|
113173
114976
|
let oblique = clone$1(this.back.obliqueRAS);
|
|
113174
|
-
multiply
|
|
114977
|
+
multiply(modelMatrix, modelMatrix, oblique);
|
|
113175
114978
|
}
|
|
113176
114979
|
let projectionMatrix = fromValues$2(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1);
|
|
113177
114980
|
let mvpMatrix = create$2();
|
|
113178
|
-
multiply
|
|
114981
|
+
multiply(mvpMatrix, projectionMatrix, modelMatrix);
|
|
113179
114982
|
var inv = create$2();
|
|
113180
114983
|
invert(inv, mvpMatrix);
|
|
113181
114984
|
var rayDir4 = fromValues(0, 0, -1, 1);
|
|
@@ -113191,7 +114994,7 @@ Niivue.prototype.calculateRayDirection = function() {
|
|
|
113191
114994
|
rayDir[2] = tiny;
|
|
113192
114995
|
return rayDir;
|
|
113193
114996
|
};
|
|
113194
|
-
Niivue.prototype.
|
|
114997
|
+
Niivue.prototype.sceneExtentsMinMax = function() {
|
|
113195
114998
|
let mn = fromValues$1(0, 0, 0);
|
|
113196
114999
|
let mx = fromValues$1(0, 0, 0);
|
|
113197
115000
|
if (this.volumes.length > 0) {
|
|
@@ -113209,11 +115012,19 @@ Niivue.prototype.setPivot3D = function() {
|
|
|
113209
115012
|
max$T(mx, mx, v);
|
|
113210
115013
|
}
|
|
113211
115014
|
}
|
|
115015
|
+
let range = create$1();
|
|
115016
|
+
subtract(range, mx, mn);
|
|
115017
|
+
return [mn, mx, range];
|
|
115018
|
+
};
|
|
115019
|
+
Niivue.prototype.setPivot3D = function() {
|
|
115020
|
+
let [mn, mx] = this.sceneExtentsMinMax();
|
|
113212
115021
|
let pivot = create$1();
|
|
113213
115022
|
add$1(pivot, mn, mx);
|
|
113214
115023
|
scale(pivot, pivot, 0.5);
|
|
113215
115024
|
this.pivot3D = [pivot[0], pivot[1], pivot[2]];
|
|
113216
115025
|
subtract(pivot, mx, mn);
|
|
115026
|
+
this.extentsMin = mn;
|
|
115027
|
+
this.extentsMax = mx;
|
|
113217
115028
|
this.furthestFromPivot = length$1(pivot) * 0.5;
|
|
113218
115029
|
};
|
|
113219
115030
|
Niivue.prototype.drawGraphLine = function(LTRB, color = [1, 0, 0, 0.5], thickness = 2) {
|
|
@@ -113230,35 +115041,22 @@ Niivue.prototype.drawGraphLine = function(LTRB, color = [1, 0, 0, 0.5], thicknes
|
|
|
113230
115041
|
this.gl.bindVertexArray(this.unusedVAO);
|
|
113231
115042
|
};
|
|
113232
115043
|
Niivue.prototype.drawRect = function(LTWH, color = [1, 0, 0, 0.5]) {
|
|
113233
|
-
this.
|
|
113234
|
-
this.gl.uniform4fv(this.
|
|
113235
|
-
this.gl.uniform2fv(this.
|
|
115044
|
+
this.rectShader.use(this.gl);
|
|
115045
|
+
this.gl.uniform4fv(this.rectShader.uniforms["lineColor"], color);
|
|
115046
|
+
this.gl.uniform2fv(this.rectShader.uniforms["canvasWidthHeight"], [
|
|
113236
115047
|
this.gl.canvas.width,
|
|
113237
115048
|
this.gl.canvas.height
|
|
113238
115049
|
]);
|
|
113239
|
-
this.gl.uniform4f(this.
|
|
115050
|
+
this.gl.uniform4f(this.rectShader.uniforms["leftTopWidthHeight"], LTWH[0], LTWH[1], LTWH[2], LTWH[3]);
|
|
113240
115051
|
this.gl.bindVertexArray(this.genericVAO);
|
|
113241
115052
|
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
113242
115053
|
this.gl.bindVertexArray(this.unusedVAO);
|
|
113243
115054
|
};
|
|
113244
|
-
function tickSpacing(tickCount, mn, mx) {
|
|
113245
|
-
let range = Math.abs(mx - mn);
|
|
113246
|
-
if (range === 0)
|
|
113247
|
-
return [0, 0];
|
|
113248
|
-
let unroundedTickSize = range / (tickCount - 1);
|
|
113249
|
-
let x2 = Math.ceil(Math.log10(unroundedTickSize) - 1);
|
|
113250
|
-
let pow10x = Math.pow(10, x2);
|
|
113251
|
-
let spacing = Math.ceil(unroundedTickSize / pow10x) * pow10x;
|
|
113252
|
-
let ticMin = mn;
|
|
113253
|
-
if (mn / spacing % 1 !== 0)
|
|
113254
|
-
ticMin = Math.sign(ticMin) * Math.round(Math.abs(ticMin));
|
|
113255
|
-
return [spacing, ticMin];
|
|
113256
|
-
}
|
|
113257
115055
|
Niivue.prototype.drawGraph = function() {
|
|
113258
115056
|
this.gl;
|
|
113259
115057
|
let graph = this.graph;
|
|
113260
115058
|
if (this.graph.autoSizeMultiplanar && this.sliceType === this.sliceTypeMultiplanar) {
|
|
113261
|
-
for (let i2 = 0; i2 < this.
|
|
115059
|
+
for (let i2 = 0; i2 < this.screenSlices.length; i2++) {
|
|
113262
115060
|
var axCorSag = this.screenSlices[i2].axCorSag;
|
|
113263
115061
|
if (axCorSag !== this.sliceTypeSagittal)
|
|
113264
115062
|
continue;
|
|
@@ -113351,7 +115149,7 @@ Niivue.prototype.drawGraph = function() {
|
|
|
113351
115149
|
graph.backColor[3]
|
|
113352
115150
|
];
|
|
113353
115151
|
this.drawRect(graph.LTWH, borderColor);
|
|
113354
|
-
let [spacing, ticMin] = tickSpacing(
|
|
115152
|
+
let [spacing, ticMin] = tickSpacing(mn, mx);
|
|
113355
115153
|
function humanize(x2) {
|
|
113356
115154
|
return x2.toFixed(6).replace(/\.?0*$/, "");
|
|
113357
115155
|
}
|
|
@@ -113415,7 +115213,7 @@ Niivue.prototype.drawGraph = function() {
|
|
|
113415
115213
|
if (i2 % 2 === 1) {
|
|
113416
115214
|
thick *= 0.5;
|
|
113417
115215
|
this.drawGraphLine([x2, plotLTWH[1], x2, plotLTWH[1] + plotLTWH[3]], thinColor, thick);
|
|
113418
|
-
} else
|
|
115216
|
+
} else {
|
|
113419
115217
|
let str = humanize(i2);
|
|
113420
115218
|
if (fntSize > 0)
|
|
113421
115219
|
this.drawTextBelow([x2, 2 + plotLTWH[1] + plotLTWH[3]], str, fntScale, graph.textColor);
|
|
@@ -113452,53 +115250,77 @@ Niivue.prototype.drawGraph = function() {
|
|
|
113452
115250
|
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);
|
|
113453
115251
|
}
|
|
113454
115252
|
};
|
|
113455
|
-
|
|
113456
|
-
|
|
115253
|
+
function isRadiological(mtx) {
|
|
115254
|
+
let vRight = fromValues(1, 0, 0, 0);
|
|
115255
|
+
let vRotated = create();
|
|
115256
|
+
transformMat4(vRotated, vRight, mtx);
|
|
115257
|
+
return vRotated[0];
|
|
115258
|
+
}
|
|
115259
|
+
function unProject(winX, winY, winZ, mvpMatrix) {
|
|
115260
|
+
let inp = fromValues(winX, winY, winZ, 1);
|
|
115261
|
+
let finalMatrix = clone$1(mvpMatrix);
|
|
115262
|
+
invert(finalMatrix, finalMatrix);
|
|
115263
|
+
inp[0] = inp[0] * 2 - 1;
|
|
115264
|
+
inp[1] = inp[1] * 2 - 1;
|
|
115265
|
+
inp[2] = inp[2] * 2 - 1;
|
|
115266
|
+
let out = create();
|
|
115267
|
+
transformMat4(out, inp, finalMatrix);
|
|
115268
|
+
if (out[3] === 0)
|
|
115269
|
+
return out;
|
|
115270
|
+
out[0] /= out[3];
|
|
115271
|
+
out[1] /= out[3];
|
|
115272
|
+
out[2] /= out[3];
|
|
115273
|
+
return out;
|
|
115274
|
+
}
|
|
115275
|
+
function unpackFloatFromVec4i(val) {
|
|
115276
|
+
var bitSh = [
|
|
115277
|
+
1 / (256 * 256 * 256),
|
|
115278
|
+
1 / (256 * 256),
|
|
115279
|
+
1 / 256,
|
|
115280
|
+
1
|
|
115281
|
+
];
|
|
115282
|
+
return (val[0] * bitSh[0] + val[1] * bitSh[1] + val[2] * bitSh[2] + val[3] * bitSh[3]) / 255;
|
|
115283
|
+
}
|
|
115284
|
+
Niivue.prototype.depthPicker = function(leftTopWidthHeight, mvpMatrix) {
|
|
115285
|
+
if (!this.scene.mouseDepthPicker)
|
|
115286
|
+
return;
|
|
115287
|
+
this.scene.mouseDepthPicker = false;
|
|
113457
115288
|
let gl = this.gl;
|
|
113458
|
-
|
|
113459
|
-
gl.
|
|
113460
|
-
|
|
113461
|
-
|
|
113462
|
-
|
|
113463
|
-
|
|
113464
|
-
|
|
115289
|
+
const pixelX = this.mousePos[0] * leftTopWidthHeight[2] / leftTopWidthHeight[2];
|
|
115290
|
+
const pixelY = gl.canvas.height - this.mousePos[1] * leftTopWidthHeight[3] / leftTopWidthHeight[3] - 1;
|
|
115291
|
+
console.log(pixelX + "/" + pixelY);
|
|
115292
|
+
const rgbaPixel = new Uint8Array(4);
|
|
115293
|
+
gl.readPixels(pixelX, pixelY, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, rgbaPixel);
|
|
115294
|
+
this.selectedObjectId = rgbaPixel[3];
|
|
115295
|
+
if (this.selectedObjectId === this.VOLUME_ID) {
|
|
115296
|
+
this.scene.crosshairPos = new Float32Array(rgbaPixel.slice(0, 3)).map((x2) => x2 / 255);
|
|
113465
115297
|
return;
|
|
113466
115298
|
}
|
|
113467
|
-
let
|
|
113468
|
-
|
|
113469
|
-
|
|
115299
|
+
let depthZ = unpackFloatFromVec4i(rgbaPixel);
|
|
115300
|
+
if (depthZ > 1)
|
|
115301
|
+
return;
|
|
115302
|
+
let fracX = (this.mousePos[0] - leftTopWidthHeight[0]) / leftTopWidthHeight[2];
|
|
115303
|
+
let fracY = (gl.canvas.height - this.mousePos[1] - leftTopWidthHeight[1]) / leftTopWidthHeight[3];
|
|
115304
|
+
let mm = unProject(fracX, fracY, depthZ, mvpMatrix);
|
|
115305
|
+
let frac = this.mm2frac(mm);
|
|
115306
|
+
if (frac[0] < 0 || frac[0] > 1 || frac[1] < 0 || frac[1] > 1 || frac[2] < 0 || frac[2] > 1)
|
|
115307
|
+
return;
|
|
115308
|
+
this.scene.crosshairPos = this.mm2frac(mm);
|
|
115309
|
+
};
|
|
115310
|
+
Niivue.prototype.drawImage3D = function(mvpMatrix, azimuth, elevation) {
|
|
115311
|
+
if (this.volumes.length === 0)
|
|
115312
|
+
return;
|
|
115313
|
+
let gl = this.gl;
|
|
115314
|
+
const rayDir = this.calculateRayDirection(azimuth, elevation);
|
|
113470
115315
|
let object3D = this.volumeObject3D;
|
|
113471
|
-
if (
|
|
113472
|
-
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
113473
|
-
this.scene.mouseDepthPicker = false;
|
|
113474
|
-
if (object3D.isVisible && object3D.isPickable) {
|
|
113475
|
-
let shader = this.pickingShader;
|
|
113476
|
-
shader.use(this.gl);
|
|
113477
|
-
gl.enable(gl.CULL_FACE);
|
|
113478
|
-
gl.cullFace(gl.FRONT);
|
|
113479
|
-
gl.uniformMatrix4fv(shader.mvpUniformLoc, false, mvpMatrix);
|
|
113480
|
-
gl.uniform3fv(shader.rayDirUniformLoc, rayDir);
|
|
113481
|
-
gl.uniform4fv(shader.clipPlaneUniformLoc, this.scene.clipPlane);
|
|
113482
|
-
gl.uniform1i(shader.uniforms["id"], object3D.id);
|
|
113483
|
-
gl.bindVertexArray(object3D.vao);
|
|
113484
|
-
gl.drawElements(object3D.mode, object3D.indexCount, gl.UNSIGNED_SHORT, 0);
|
|
113485
|
-
gl.bindVertexArray(this.unusedVAO);
|
|
113486
|
-
}
|
|
113487
|
-
const pixelX = this.mousePos[0] * gl.canvas.width / gl.canvas.clientWidth;
|
|
113488
|
-
const pixelY = gl.canvas.height - this.mousePos[1] * gl.canvas.height / gl.canvas.clientHeight - 1;
|
|
113489
|
-
const rgbaPixel = new Uint8Array(4);
|
|
113490
|
-
gl.readPixels(pixelX, pixelY, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, rgbaPixel);
|
|
113491
|
-
this.selectedObjectId = rgbaPixel[3];
|
|
113492
|
-
if (this.selectedObjectId === this.VOLUME_ID) {
|
|
113493
|
-
this.scene.crosshairPos = new Float32Array(rgbaPixel.slice(0, 3)).map((x2) => x2 / 255);
|
|
113494
|
-
}
|
|
113495
|
-
}
|
|
113496
|
-
if (this.volumeObject3D.isVisible) {
|
|
115316
|
+
if (object3D) {
|
|
113497
115317
|
gl.enable(gl.BLEND);
|
|
113498
115318
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
113499
115319
|
gl.enable(gl.CULL_FACE);
|
|
113500
115320
|
gl.cullFace(gl.FRONT);
|
|
113501
115321
|
let shader = this.renderShader;
|
|
115322
|
+
if (this.scene.mouseDepthPicker)
|
|
115323
|
+
shader = this.pickingImageShader;
|
|
113502
115324
|
shader.use(this.gl);
|
|
113503
115325
|
gl.uniform1i(shader.uniforms["backgroundMasksOverlays"], this.backgroundMasksOverlays);
|
|
113504
115326
|
gl.uniformMatrix4fv(shader.mvpUniformLoc, false, mvpMatrix);
|
|
@@ -113509,22 +115331,99 @@ Niivue.prototype.draw3D = function() {
|
|
|
113509
115331
|
gl.drawElements(object3D.mode, object3D.indexCount, gl.UNSIGNED_SHORT, 0);
|
|
113510
115332
|
gl.bindVertexArray(this.unusedVAO);
|
|
113511
115333
|
}
|
|
113512
|
-
|
|
113513
|
-
|
|
113514
|
-
this.
|
|
113515
|
-
|
|
115334
|
+
};
|
|
115335
|
+
Niivue.prototype.drawOrientationCube = function(leftTopWidthHeight, azimuth = 0, elevation = 0) {
|
|
115336
|
+
if (!this.opts.isOrientCube)
|
|
115337
|
+
return;
|
|
115338
|
+
let sz = 0.05 * Math.min(leftTopWidthHeight[2], leftTopWidthHeight[3]);
|
|
115339
|
+
if (sz < 5)
|
|
115340
|
+
return;
|
|
115341
|
+
let gl = this.gl;
|
|
115342
|
+
gl.enable(gl.CULL_FACE);
|
|
115343
|
+
gl.cullFace(gl.BACK);
|
|
115344
|
+
this.orientCubeShader.use(this.gl);
|
|
115345
|
+
gl.bindVertexArray(this.orientCubeShaderVAO);
|
|
115346
|
+
function deg2rad2(deg) {
|
|
115347
|
+
return deg * (Math.PI / 180);
|
|
115348
|
+
}
|
|
115349
|
+
let modelMatrix = create$2();
|
|
115350
|
+
let projectionMatrix = create$2();
|
|
115351
|
+
ortho(projectionMatrix, 0, gl.canvas.width, 0, gl.canvas.height, -10 * sz, 10 * sz);
|
|
115352
|
+
translate(modelMatrix, modelMatrix, [
|
|
115353
|
+
1.8 * sz + leftTopWidthHeight[0],
|
|
115354
|
+
1.8 * sz + leftTopWidthHeight[1],
|
|
115355
|
+
0
|
|
115356
|
+
]);
|
|
115357
|
+
scale$1(modelMatrix, modelMatrix, [sz, sz, sz]);
|
|
115358
|
+
rotateX(modelMatrix, modelMatrix, deg2rad2(270 - elevation));
|
|
115359
|
+
rotateZ(modelMatrix, modelMatrix, deg2rad2(-azimuth));
|
|
115360
|
+
let modelViewProjectionMatrix = create$2();
|
|
115361
|
+
multiply(modelViewProjectionMatrix, projectionMatrix, modelMatrix);
|
|
115362
|
+
gl.uniformMatrix4fv(this.orientCubeMtxLoc, false, modelViewProjectionMatrix);
|
|
115363
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 168);
|
|
115364
|
+
gl.bindVertexArray(this.unusedVAO);
|
|
115365
|
+
};
|
|
115366
|
+
Niivue.prototype.draw3D = function(leftTopWidthHeight = [0, 0, 0, 0], mvpMatrix = null, modelMatrix, normalMatrix, azimuth = null, elevation) {
|
|
115367
|
+
let isMosaic = azimuth !== null;
|
|
115368
|
+
this.setPivot3D();
|
|
115369
|
+
if (!isMosaic) {
|
|
115370
|
+
azimuth = this.scene.renderAzimuth;
|
|
115371
|
+
elevation = this.scene.renderElevation;
|
|
115372
|
+
}
|
|
115373
|
+
let gl = this.gl;
|
|
115374
|
+
if (mvpMatrix === null)
|
|
115375
|
+
[mvpMatrix, modelMatrix, normalMatrix] = this.calculateMvpMatrix(null, leftTopWidthHeight, azimuth, elevation);
|
|
115376
|
+
if (leftTopWidthHeight[2] === 0 || leftTopWidthHeight[3] === 0) {
|
|
115377
|
+
leftTopWidthHeight = [0, 0, gl.canvas.width, gl.canvas.height];
|
|
115378
|
+
this.screenSlices.push({
|
|
115379
|
+
leftTopWidthHeight,
|
|
115380
|
+
axCorSag: this.sliceTypeRender,
|
|
115381
|
+
sliceFrac: 0,
|
|
115382
|
+
AxyzMxy: [],
|
|
115383
|
+
leftTopMM: [],
|
|
115384
|
+
fovMM: [isRadiological(modelMatrix), 0]
|
|
115385
|
+
});
|
|
115386
|
+
} else {
|
|
115387
|
+
this.screenSlices.push({
|
|
115388
|
+
leftTopWidthHeight: leftTopWidthHeight.slice(),
|
|
115389
|
+
axCorSag: this.sliceTypeRender,
|
|
115390
|
+
sliceFrac: 0,
|
|
115391
|
+
AxyzMxy: [],
|
|
115392
|
+
leftTopMM: [],
|
|
115393
|
+
fovMM: [isRadiological(modelMatrix), 0]
|
|
115394
|
+
});
|
|
115395
|
+
leftTopWidthHeight[1] = gl.canvas.height - leftTopWidthHeight[3] - leftTopWidthHeight[1];
|
|
115396
|
+
}
|
|
115397
|
+
gl.viewport(leftTopWidthHeight[0], leftTopWidthHeight[1], leftTopWidthHeight[2], leftTopWidthHeight[3]);
|
|
115398
|
+
gl.enable(gl.DEPTH_TEST);
|
|
115399
|
+
gl.depthFunc(gl.ALWAYS);
|
|
115400
|
+
gl.clearDepth(0);
|
|
115401
|
+
if (this.volumes.length > 0)
|
|
115402
|
+
this.drawImage3D(mvpMatrix, azimuth, elevation);
|
|
115403
|
+
if (!isMosaic)
|
|
115404
|
+
this.drawCrosshairs3D(true, 1, mvpMatrix);
|
|
115405
|
+
this.drawMesh3D(true, 1, mvpMatrix, modelMatrix, normalMatrix);
|
|
115406
|
+
if (this.scene.mouseDepthPicker) {
|
|
115407
|
+
this.depthPicker(leftTopWidthHeight, mvpMatrix, true);
|
|
115408
|
+
return;
|
|
115409
|
+
}
|
|
115410
|
+
this.drawMesh3D(false, 0.02, mvpMatrix, modelMatrix, normalMatrix);
|
|
115411
|
+
if (!isMosaic)
|
|
115412
|
+
this.drawCrosshairs3D(false, 0.15, mvpMatrix);
|
|
115413
|
+
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
|
115414
|
+
this.drawOrientationCube(leftTopWidthHeight, azimuth, elevation);
|
|
113516
115415
|
let posString = "azimuth: " + this.scene.renderAzimuth.toFixed(0) + " elevation: " + this.scene.renderElevation.toFixed(0);
|
|
113517
115416
|
this.drawGraph();
|
|
113518
115417
|
this.readyForSync = true;
|
|
113519
115418
|
this.sync();
|
|
113520
115419
|
return posString;
|
|
113521
115420
|
};
|
|
113522
|
-
Niivue.prototype.drawMesh3D = function(isDepthTest = true, alpha = 1) {
|
|
115421
|
+
Niivue.prototype.drawMesh3D = function(isDepthTest = true, alpha = 1, m, modelMtx, normMtx) {
|
|
113523
115422
|
if (this.meshes.length < 1)
|
|
113524
115423
|
return;
|
|
113525
115424
|
let gl = this.gl;
|
|
113526
|
-
|
|
113527
|
-
|
|
115425
|
+
if (!m)
|
|
115426
|
+
[m, modelMtx, normMtx] = this.calculateMvpMatrix(this.volumeObject3D, this.scene.renderAzimuth, this.scene.renderElevation);
|
|
113528
115427
|
gl.enable(gl.DEPTH_TEST);
|
|
113529
115428
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
113530
115429
|
gl.disable(gl.BLEND);
|
|
@@ -113537,11 +115436,14 @@ Niivue.prototype.drawMesh3D = function(isDepthTest = true, alpha = 1) {
|
|
|
113537
115436
|
gl.depthFunc(gl.ALWAYS);
|
|
113538
115437
|
}
|
|
113539
115438
|
gl.disable(gl.CULL_FACE);
|
|
113540
|
-
this.meshShader
|
|
113541
|
-
|
|
113542
|
-
|
|
113543
|
-
|
|
113544
|
-
gl.
|
|
115439
|
+
let shader = this.meshShader;
|
|
115440
|
+
if (this.scene.mouseDepthPicker)
|
|
115441
|
+
shader = this.pickingMeshShader;
|
|
115442
|
+
shader.use(this.gl);
|
|
115443
|
+
gl.uniformMatrix4fv(shader.uniforms["mvpMtx"], false, m);
|
|
115444
|
+
gl.uniformMatrix4fv(shader.uniforms["modelMtx"], false, modelMtx);
|
|
115445
|
+
gl.uniformMatrix4fv(shader.uniforms["normMtx"], false, normMtx);
|
|
115446
|
+
gl.uniform1f(shader.uniforms["opacity"], alpha);
|
|
113545
115447
|
let hasFibers = false;
|
|
113546
115448
|
for (let i2 = 0; i2 < this.meshes.length; i2++) {
|
|
113547
115449
|
if (this.meshes[i2].indexCount < 3)
|
|
@@ -113559,7 +115461,7 @@ Niivue.prototype.drawMesh3D = function(isDepthTest = true, alpha = 1) {
|
|
|
113559
115461
|
gl.depthFunc(gl.ALWAYS);
|
|
113560
115462
|
return;
|
|
113561
115463
|
}
|
|
113562
|
-
|
|
115464
|
+
shader = this.fiberShader;
|
|
113563
115465
|
shader.use(this.gl);
|
|
113564
115466
|
gl.uniformMatrix4fv(shader.uniforms["mvpMtx"], false, m);
|
|
113565
115467
|
gl.uniform1f(shader.uniforms["opacity"], alpha);
|
|
@@ -113576,7 +115478,7 @@ Niivue.prototype.drawMesh3D = function(isDepthTest = true, alpha = 1) {
|
|
|
113576
115478
|
gl.depthFunc(gl.ALWAYS);
|
|
113577
115479
|
this.readyForSync = true;
|
|
113578
115480
|
};
|
|
113579
|
-
Niivue.prototype.drawCrosshairs3D = function(isDepthTest = true, alpha = 1) {
|
|
115481
|
+
Niivue.prototype.drawCrosshairs3D = function(isDepthTest = true, alpha = 1, mvpMtx = null) {
|
|
113580
115482
|
if (!this.opts.show3Dcrosshair)
|
|
113581
115483
|
return;
|
|
113582
115484
|
let gl = this.gl;
|
|
@@ -113586,15 +115488,21 @@ Niivue.prototype.drawCrosshairs3D = function(isDepthTest = true, alpha = 1) {
|
|
|
113586
115488
|
gl.deleteBuffer(this.crosshairs3D.indexBuffer);
|
|
113587
115489
|
gl.deleteBuffer(this.crosshairs3D.vertexBuffer);
|
|
113588
115490
|
}
|
|
113589
|
-
let
|
|
113590
|
-
|
|
115491
|
+
let [mn, mx, range] = this.sceneExtentsMinMax();
|
|
115492
|
+
let radius = 1;
|
|
115493
|
+
if (this.volumes.length > 0)
|
|
115494
|
+
radius = 0.5 * Math.min(Math.min(this.back.pixDims[1], this.back.pixDims[2]), this.back.pixDims[3]);
|
|
115495
|
+
else if (range[0] < 50 || range[0] > 1e3)
|
|
115496
|
+
radius = range[0] * 0.02;
|
|
115497
|
+
this.crosshairs3D = NiivueObject3D.generateCrosshairs(this.gl, 1, mm, mn, mx, radius);
|
|
113591
115498
|
this.crosshairs3D.mm = mm;
|
|
113592
115499
|
}
|
|
113593
115500
|
let crosshairsShader = this.surfaceShader;
|
|
113594
115501
|
crosshairsShader.use(this.gl);
|
|
113595
|
-
let
|
|
113596
|
-
|
|
113597
|
-
|
|
115502
|
+
let modelMtx, normMtx;
|
|
115503
|
+
if (mvpMtx == null)
|
|
115504
|
+
[mvpMtx, modelMtx, normMtx] = this.calculateMvpMatrix(this.crosshairs3D, this.scene.renderAzimuth, this.scene.renderElevation);
|
|
115505
|
+
gl.uniformMatrix4fv(crosshairsShader.uniforms["mvpMtx"], false, mvpMtx);
|
|
113598
115506
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.crosshairs3D.indexBuffer);
|
|
113599
115507
|
gl.enable(gl.DEPTH_TEST);
|
|
113600
115508
|
let color = [...this.opts.crosshairColor];
|
|
@@ -113613,6 +115521,14 @@ Niivue.prototype.drawCrosshairs3D = function(isDepthTest = true, alpha = 1) {
|
|
|
113613
115521
|
gl.bindVertexArray(this.unusedVAO);
|
|
113614
115522
|
};
|
|
113615
115523
|
Niivue.prototype.mm2frac = function(mm, volIdx = 0) {
|
|
115524
|
+
if (this.volumes.length < 1) {
|
|
115525
|
+
let frac2 = [0.1, 0.5, 0.5];
|
|
115526
|
+
let [mn, mx, range] = this.sceneExtentsMinMax();
|
|
115527
|
+
frac2[0] = (mm[0] - mn[0]) / range[0];
|
|
115528
|
+
frac2[1] = (mm[1] - mn[1]) / range[1];
|
|
115529
|
+
frac2[2] = (mm[2] - mn[2]) / range[2];
|
|
115530
|
+
return frac2;
|
|
115531
|
+
}
|
|
113616
115532
|
let mm4 = fromValues(mm[0], mm[1], mm[2], 1);
|
|
113617
115533
|
let d = this.volumes[volIdx].dimsRAS;
|
|
113618
115534
|
let frac = [0, 0, 0];
|
|
@@ -113656,69 +115572,87 @@ Niivue.prototype.moveCrosshairInVox = function(x2, y, z) {
|
|
|
113656
115572
|
};
|
|
113657
115573
|
Niivue.prototype.frac2mm = function(frac, volIdx = 0) {
|
|
113658
115574
|
let pos = fromValues(frac[0], frac[1], frac[2], 1);
|
|
113659
|
-
|
|
113660
|
-
|
|
113661
|
-
|
|
113662
|
-
|
|
113663
|
-
|
|
113664
|
-
|
|
113665
|
-
|
|
115575
|
+
if (this.volumes.length > 0)
|
|
115576
|
+
transformMat4(pos, pos, this.volumes[volIdx].frac2mm);
|
|
115577
|
+
else {
|
|
115578
|
+
let [mn, mx] = this.sceneExtentsMinMax();
|
|
115579
|
+
const lerp2 = (x2, y, a) => x2 * (1 - a) + y * a;
|
|
115580
|
+
pos[0] = lerp2(mn[0], mx[0], frac[0]);
|
|
115581
|
+
pos[1] = lerp2(mn[1], mx[1], frac[1]);
|
|
115582
|
+
pos[2] = lerp2(mn[2], mx[2], frac[2]);
|
|
115583
|
+
}
|
|
113666
115584
|
return pos;
|
|
113667
115585
|
};
|
|
113668
|
-
Niivue.prototype.
|
|
113669
|
-
let
|
|
113670
|
-
|
|
113671
|
-
|
|
113672
|
-
|
|
113673
|
-
|
|
113674
|
-
|
|
113675
|
-
|
|
113676
|
-
|
|
113677
|
-
|
|
113678
|
-
|
|
113679
|
-
|
|
113680
|
-
|
|
113681
|
-
|
|
113682
|
-
|
|
113683
|
-
|
|
113684
|
-
|
|
113685
|
-
|
|
113686
|
-
|
|
113687
|
-
|
|
113688
|
-
|
|
113689
|
-
|
|
113690
|
-
|
|
113691
|
-
|
|
113692
|
-
|
|
113693
|
-
|
|
113694
|
-
|
|
113695
|
-
|
|
113696
|
-
|
|
113697
|
-
|
|
113698
|
-
|
|
113699
|
-
|
|
113700
|
-
|
|
113701
|
-
|
|
113702
|
-
|
|
113703
|
-
|
|
113704
|
-
|
|
113705
|
-
|
|
113706
|
-
|
|
113707
|
-
|
|
113708
|
-
|
|
115586
|
+
Niivue.prototype.screenXY2TextureFrac = function(x2, y, i2) {
|
|
115587
|
+
let texFrac = [-1, -1, -1];
|
|
115588
|
+
var axCorSag = this.screenSlices[i2].axCorSag;
|
|
115589
|
+
if (axCorSag > this.sliceTypeSagittal)
|
|
115590
|
+
return texFrac;
|
|
115591
|
+
var ltwh = this.screenSlices[i2].leftTopWidthHeight.slice();
|
|
115592
|
+
let isMirror = false;
|
|
115593
|
+
if (ltwh[2] < 0) {
|
|
115594
|
+
isMirror = true;
|
|
115595
|
+
ltwh[0] += ltwh[2];
|
|
115596
|
+
ltwh[2] = -ltwh[2];
|
|
115597
|
+
}
|
|
115598
|
+
var fracX = (x2 - ltwh[0]) / ltwh[2];
|
|
115599
|
+
if (isMirror)
|
|
115600
|
+
fracX = 1 - fracX;
|
|
115601
|
+
var fracY = 1 - (y - ltwh[1]) / ltwh[3];
|
|
115602
|
+
if (fracX < 0 || fracX > 1 || fracY < 0 || fracY > 1)
|
|
115603
|
+
return texFrac;
|
|
115604
|
+
if (this.screenSlices[i2].AxyzMxy.length > 4) {
|
|
115605
|
+
let xyzMM = [0, 0, 0];
|
|
115606
|
+
xyzMM[0] = this.screenSlices[i2].leftTopMM[0] + fracX * this.screenSlices[i2].fovMM[0];
|
|
115607
|
+
xyzMM[1] = this.screenSlices[i2].leftTopMM[1] + fracY * this.screenSlices[i2].fovMM[1];
|
|
115608
|
+
let v = this.screenSlices[i2].AxyzMxy;
|
|
115609
|
+
xyzMM[2] = v[2] + v[4] * (xyzMM[1] - v[1]) - v[3] * (xyzMM[0] - v[0]);
|
|
115610
|
+
if (axCorSag === this.sliceTypeCoronal)
|
|
115611
|
+
xyzMM = swizzleVec3(xyzMM, [0, 2, 1]);
|
|
115612
|
+
if (axCorSag === this.sliceTypeSagittal)
|
|
115613
|
+
xyzMM = swizzleVec3(xyzMM, [2, 0, 1]);
|
|
115614
|
+
let xyz = this.mm2frac(xyzMM);
|
|
115615
|
+
if (xyz[0] < 0 || xyz[0] > 1 || xyz[1] < 0 || xyz[1] > 1 || xyz[2] < 0 || xyz[2] > 1)
|
|
115616
|
+
return texFrac;
|
|
115617
|
+
texFrac[0] = xyz[0];
|
|
115618
|
+
texFrac[1] = xyz[1];
|
|
115619
|
+
texFrac[2] = xyz[2];
|
|
115620
|
+
} else {
|
|
115621
|
+
texFrac = this.scene.crosshairPos.slice();
|
|
115622
|
+
if (axCorSag === this.sliceTypeAxial) {
|
|
115623
|
+
texFrac[0] = fracX;
|
|
115624
|
+
texFrac[1] = fracY;
|
|
115625
|
+
}
|
|
115626
|
+
if (axCorSag === this.sliceTypeCoronal) {
|
|
115627
|
+
texFrac[0] = fracX;
|
|
115628
|
+
texFrac[2] = fracY;
|
|
113709
115629
|
}
|
|
115630
|
+
if (axCorSag === this.sliceTypeSagittal) {
|
|
115631
|
+
texFrac[1] = fracX;
|
|
115632
|
+
texFrac[2] = fracY;
|
|
115633
|
+
}
|
|
115634
|
+
}
|
|
115635
|
+
return texFrac;
|
|
115636
|
+
};
|
|
115637
|
+
Niivue.prototype.canvasPos2frac = function(canvasPos) {
|
|
115638
|
+
for (let i2 = 0; i2 < this.screenSlices.length; i2++) {
|
|
115639
|
+
let texFrac = this.screenXY2TextureFrac(canvasPos[0], canvasPos[1], i2);
|
|
115640
|
+
if (texFrac[0] >= 0)
|
|
115641
|
+
return texFrac;
|
|
113710
115642
|
}
|
|
113711
|
-
return [
|
|
115643
|
+
return [-1, -1, -1];
|
|
113712
115644
|
};
|
|
113713
|
-
Niivue.prototype.scaleSlice = function(w, h) {
|
|
113714
|
-
let
|
|
113715
|
-
|
|
113716
|
-
|
|
115645
|
+
Niivue.prototype.scaleSlice = function(w, h, widthPadPixels = 0, heightPadPixels = 0) {
|
|
115646
|
+
let canvasW = this.gl.canvas.clientWidth - widthPadPixels;
|
|
115647
|
+
let canvasH = this.effectiveCanvasHeight() - heightPadPixels;
|
|
115648
|
+
let scalePix = canvasW / w;
|
|
115649
|
+
if (h * scalePix > canvasH)
|
|
115650
|
+
scalePix = canvasH / h;
|
|
113717
115651
|
let wPix = w * scalePix;
|
|
113718
115652
|
let hPix = h * scalePix;
|
|
113719
115653
|
let leftTopWidthHeight = [
|
|
113720
|
-
(
|
|
113721
|
-
(
|
|
115654
|
+
(canvasW - wPix) * 0.5,
|
|
115655
|
+
(canvasH - hPix) * 0.5,
|
|
113722
115656
|
wPix,
|
|
113723
115657
|
hPix
|
|
113724
115658
|
];
|
|
@@ -113738,12 +115672,326 @@ Niivue.prototype.drawThumbnail = function() {
|
|
|
113738
115672
|
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
113739
115673
|
this.gl.bindVertexArray(this.unusedVAO);
|
|
113740
115674
|
};
|
|
115675
|
+
Niivue.prototype.drawLine = function(leftTopWidthHeight) {
|
|
115676
|
+
this.gl.enable(this.gl.BLEND);
|
|
115677
|
+
this.rectShader.use(this.gl);
|
|
115678
|
+
this.gl.uniform4fv(this.rectShader.uniforms["lineColor"], this.opts.crosshairColor);
|
|
115679
|
+
this.gl.uniform2fv(this.rectShader.uniforms["canvasWidthHeight"], [
|
|
115680
|
+
this.gl.canvas.width,
|
|
115681
|
+
this.gl.canvas.height
|
|
115682
|
+
]);
|
|
115683
|
+
this.gl.uniform4f(this.rectShader.uniforms["leftTopWidthHeight"], leftTopWidthHeight[0], leftTopWidthHeight[1], leftTopWidthHeight[2], leftTopWidthHeight[3]);
|
|
115684
|
+
this.gl.bindVertexArray(this.genericVAO);
|
|
115685
|
+
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
115686
|
+
this.gl.bindVertexArray(this.unusedVAO);
|
|
115687
|
+
};
|
|
115688
|
+
Niivue.prototype.drawLine2DX = function(startXYendXY, thickness = 1) {
|
|
115689
|
+
this.gl.bindVertexArray(this.genericVAO);
|
|
115690
|
+
this.lineShader.use(this.gl);
|
|
115691
|
+
this.gl.uniform4fv(this.lineShader.uniforms["lineColor"], this.opts.crosshairColor);
|
|
115692
|
+
this.gl.uniform2fv(this.lineShader.uniforms["canvasWidthHeight"], [
|
|
115693
|
+
this.gl.canvas.width,
|
|
115694
|
+
this.gl.canvas.height
|
|
115695
|
+
]);
|
|
115696
|
+
this.gl.uniform1f(this.lineShader.uniforms["thickness"], thickness);
|
|
115697
|
+
this.gl.uniform4fv(this.lineShader.uniforms["startXYendXY"], startXYendXY);
|
|
115698
|
+
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
|
|
115699
|
+
this.gl.bindVertexArray(this.unusedVAO);
|
|
115700
|
+
};
|
|
115701
|
+
Niivue.prototype.drawCrossLinesMM = function(sliceIndex, axCorSag, axiMM, corMM, sagMM) {
|
|
115702
|
+
if (sliceIndex < 0 || this.screenSlices.length <= sliceIndex)
|
|
115703
|
+
return;
|
|
115704
|
+
let tile = this.screenSlices[sliceIndex];
|
|
115705
|
+
let linesH = corMM.slice();
|
|
115706
|
+
let linesV = sagMM.slice();
|
|
115707
|
+
let thick = Math.max(1, this.opts.crosshairWidth);
|
|
115708
|
+
if (axCorSag === this.sliceTypeCoronal)
|
|
115709
|
+
linesH = axiMM.slice();
|
|
115710
|
+
if (axCorSag === this.sliceTypeSagittal) {
|
|
115711
|
+
linesH = axiMM.slice();
|
|
115712
|
+
linesV = corMM.slice();
|
|
115713
|
+
}
|
|
115714
|
+
function mm2screen(mm) {
|
|
115715
|
+
let screenXY = [0, 0];
|
|
115716
|
+
screenXY[0] = tile.leftTopWidthHeight[0] + (mm[0] - tile.leftTopMM[0]) / tile.fovMM[0] * tile.leftTopWidthHeight[2];
|
|
115717
|
+
screenXY[1] = tile.leftTopWidthHeight[1] + tile.leftTopWidthHeight[3] - (mm[1] - tile.leftTopMM[1]) / tile.fovMM[1] * tile.leftTopWidthHeight[3];
|
|
115718
|
+
return screenXY;
|
|
115719
|
+
}
|
|
115720
|
+
if (linesH.length > 0 && axCorSag === 0) {
|
|
115721
|
+
let fracZ = tile.sliceFrac;
|
|
115722
|
+
let dimV = 1;
|
|
115723
|
+
for (let i2 = 0; i2 < linesH.length; i2++) {
|
|
115724
|
+
let mmV = this.frac2mm([0.5, 0.5, 0.5]);
|
|
115725
|
+
mmV[dimV] = linesH[i2];
|
|
115726
|
+
let fracY = this.mm2frac(mmV);
|
|
115727
|
+
fracY = fracY[dimV];
|
|
115728
|
+
let left = this.frac2mm([0, fracY, fracZ]);
|
|
115729
|
+
left = swizzleVec3(left, [0, 1, 2]);
|
|
115730
|
+
let right = this.frac2mm([1, fracY, fracZ]);
|
|
115731
|
+
right = swizzleVec3(right, [0, 1, 2]);
|
|
115732
|
+
left = mm2screen(left);
|
|
115733
|
+
right = mm2screen(right);
|
|
115734
|
+
this.drawLine2DX([left[0], left[1], right[0], right[1]], thick);
|
|
115735
|
+
}
|
|
115736
|
+
}
|
|
115737
|
+
if (linesH.length > 0 && axCorSag === 1) {
|
|
115738
|
+
let fracH = tile.sliceFrac;
|
|
115739
|
+
this.frac2mm([0.5, fracH, 0.5]);
|
|
115740
|
+
let dimV = 2;
|
|
115741
|
+
for (let i2 = 0; i2 < linesH.length; i2++) {
|
|
115742
|
+
let mmV = this.frac2mm([0.5, 0.5, 0.5]);
|
|
115743
|
+
mmV[dimV] = linesH[i2];
|
|
115744
|
+
let fracV = this.mm2frac(mmV);
|
|
115745
|
+
fracV = fracV[dimV];
|
|
115746
|
+
let left = this.frac2mm([0, fracH, fracV]);
|
|
115747
|
+
left = swizzleVec3(left, [0, 2, 1]);
|
|
115748
|
+
let right = this.frac2mm([1, fracH, fracV]);
|
|
115749
|
+
right = swizzleVec3(right, [0, 2, 1]);
|
|
115750
|
+
left = mm2screen(left);
|
|
115751
|
+
right = mm2screen(right);
|
|
115752
|
+
this.drawLine2DX([left[0], left[1], right[0], right[1]], thick);
|
|
115753
|
+
}
|
|
115754
|
+
}
|
|
115755
|
+
if (linesH.length > 0 && axCorSag === 2) {
|
|
115756
|
+
let fracX = tile.sliceFrac;
|
|
115757
|
+
let dimV = 2;
|
|
115758
|
+
for (let i2 = 0; i2 < linesH.length; i2++) {
|
|
115759
|
+
let mmV = this.frac2mm([0.5, 0.5, 0.5]);
|
|
115760
|
+
mmV[dimV] = linesH[i2];
|
|
115761
|
+
let fracZ = this.mm2frac(mmV);
|
|
115762
|
+
fracZ = fracZ[dimV];
|
|
115763
|
+
let left = this.frac2mm([fracX, 0, fracZ]);
|
|
115764
|
+
left = swizzleVec3(left, [1, 2, 0]);
|
|
115765
|
+
let right = this.frac2mm([fracX, 1, fracZ]);
|
|
115766
|
+
right = swizzleVec3(right, [1, 2, 0]);
|
|
115767
|
+
left = mm2screen(left);
|
|
115768
|
+
right = mm2screen(right);
|
|
115769
|
+
this.drawLine2DX([left[0], left[1], right[0], right[1]], thick);
|
|
115770
|
+
}
|
|
115771
|
+
}
|
|
115772
|
+
if (linesV.length > 0 && axCorSag === 0) {
|
|
115773
|
+
let fracZ = tile.sliceFrac;
|
|
115774
|
+
let dimH = 0;
|
|
115775
|
+
for (let i2 = 0; i2 < linesV.length; i2++) {
|
|
115776
|
+
let mm = this.frac2mm([0.5, 0.5, 0.5]);
|
|
115777
|
+
mm[dimH] = linesV[i2];
|
|
115778
|
+
let frac = this.mm2frac(mm);
|
|
115779
|
+
frac = frac[dimH];
|
|
115780
|
+
let left = this.frac2mm([frac, 0, fracZ]);
|
|
115781
|
+
left = swizzleVec3(left, [0, 1, 2]);
|
|
115782
|
+
let right = this.frac2mm([frac, 1, fracZ]);
|
|
115783
|
+
right = swizzleVec3(right, [0, 1, 2]);
|
|
115784
|
+
left = mm2screen(left);
|
|
115785
|
+
right = mm2screen(right);
|
|
115786
|
+
this.drawLine2DX([left[0], left[1], right[0], right[1]], thick);
|
|
115787
|
+
}
|
|
115788
|
+
}
|
|
115789
|
+
if (linesV.length > 0 && axCorSag === 1) {
|
|
115790
|
+
let fracY = tile.sliceFrac;
|
|
115791
|
+
let dimH = 0;
|
|
115792
|
+
for (let i2 = 0; i2 < linesV.length; i2++) {
|
|
115793
|
+
let mm = this.frac2mm([0.5, 0.5, 0.5]);
|
|
115794
|
+
mm[dimH] = linesV[i2];
|
|
115795
|
+
let frac = this.mm2frac(mm);
|
|
115796
|
+
frac = frac[dimH];
|
|
115797
|
+
let left = this.frac2mm([frac, fracY, 0]);
|
|
115798
|
+
left = swizzleVec3(left, [0, 2, 1]);
|
|
115799
|
+
let right = this.frac2mm([frac, fracY, 1]);
|
|
115800
|
+
right = swizzleVec3(right, [0, 2, 1]);
|
|
115801
|
+
left = mm2screen(left);
|
|
115802
|
+
right = mm2screen(right);
|
|
115803
|
+
this.drawLine2DX([left[0], left[1], right[0], right[1]], thick);
|
|
115804
|
+
}
|
|
115805
|
+
}
|
|
115806
|
+
if (linesV.length > 0 && axCorSag === 2) {
|
|
115807
|
+
let fracX = tile.sliceFrac;
|
|
115808
|
+
let dimH = 1;
|
|
115809
|
+
for (let i2 = 0; i2 < linesV.length; i2++) {
|
|
115810
|
+
let mm = this.frac2mm([0.5, 0.5, 0.5]);
|
|
115811
|
+
mm[dimH] = linesV[i2];
|
|
115812
|
+
let frac = this.mm2frac(mm);
|
|
115813
|
+
frac = frac[dimH];
|
|
115814
|
+
let left = this.frac2mm([fracX, frac, 0]);
|
|
115815
|
+
left = swizzleVec3(left, [1, 2, 0]);
|
|
115816
|
+
let right = this.frac2mm([fracX, frac, 1]);
|
|
115817
|
+
right = swizzleVec3(right, [1, 2, 0]);
|
|
115818
|
+
left = mm2screen(left);
|
|
115819
|
+
right = mm2screen(right);
|
|
115820
|
+
this.drawLine2DX([left[0], left[1], right[0], right[1]], thick);
|
|
115821
|
+
}
|
|
115822
|
+
}
|
|
115823
|
+
};
|
|
115824
|
+
Niivue.prototype.drawCrossLines = function(sliceIndex, axCorSag, axiMM, corMM, sagMM) {
|
|
115825
|
+
if (sliceIndex < 0 || this.screenSlices.length <= sliceIndex)
|
|
115826
|
+
return;
|
|
115827
|
+
if (this.opts.isSliceMM)
|
|
115828
|
+
return this.drawCrossLinesMM(sliceIndex, axCorSag, axiMM, corMM, sagMM);
|
|
115829
|
+
let tile = this.screenSlices[sliceIndex];
|
|
115830
|
+
let linesH = corMM.slice();
|
|
115831
|
+
let linesV = sagMM.slice();
|
|
115832
|
+
Math.max(1, this.opts.crosshairWidth);
|
|
115833
|
+
if (axCorSag === this.sliceTypeCoronal)
|
|
115834
|
+
linesH = axiMM.slice();
|
|
115835
|
+
if (axCorSag === this.sliceTypeSagittal) {
|
|
115836
|
+
linesH = axiMM.slice();
|
|
115837
|
+
linesV = corMM.slice();
|
|
115838
|
+
}
|
|
115839
|
+
if (linesH.length > 0) {
|
|
115840
|
+
let LTWH = tile.leftTopWidthHeight.slice();
|
|
115841
|
+
let sliceDim = 2;
|
|
115842
|
+
let mm = this.frac2mm([0.5, 0.5, 0.5]);
|
|
115843
|
+
for (let i2 = 0; i2 < linesH.length; i2++) {
|
|
115844
|
+
mm[sliceDim] = linesH[i2];
|
|
115845
|
+
let frac = this.mm2frac(mm);
|
|
115846
|
+
this.drawLine([
|
|
115847
|
+
LTWH[0],
|
|
115848
|
+
LTWH[1] + LTWH[3] - frac[sliceDim] * LTWH[3],
|
|
115849
|
+
LTWH[2],
|
|
115850
|
+
1
|
|
115851
|
+
]);
|
|
115852
|
+
}
|
|
115853
|
+
}
|
|
115854
|
+
if (linesV.length > 0) {
|
|
115855
|
+
let LTWH = tile.leftTopWidthHeight.slice();
|
|
115856
|
+
let isRadiolgical = tile.fovMM[0] < 0;
|
|
115857
|
+
let sliceDim = 0;
|
|
115858
|
+
if (axCorSag === this.sliceTypeSagittal)
|
|
115859
|
+
sliceDim = 1;
|
|
115860
|
+
let mm = this.frac2mm([0.5, 0.5, 0.5]);
|
|
115861
|
+
for (let i2 = 0; i2 < linesV.length; i2++) {
|
|
115862
|
+
mm[sliceDim] = linesV[i2];
|
|
115863
|
+
let frac = this.mm2frac(mm);
|
|
115864
|
+
if (isRadiolgical)
|
|
115865
|
+
this.drawLine([
|
|
115866
|
+
LTWH[0] + (LTWH[2] - frac[sliceDim] * LTWH[2]),
|
|
115867
|
+
LTWH[1],
|
|
115868
|
+
1,
|
|
115869
|
+
LTWH[3]
|
|
115870
|
+
]);
|
|
115871
|
+
else
|
|
115872
|
+
this.drawLine([
|
|
115873
|
+
LTWH[0] + frac[sliceDim] * LTWH[2],
|
|
115874
|
+
LTWH[1],
|
|
115875
|
+
1,
|
|
115876
|
+
LTWH[3]
|
|
115877
|
+
]);
|
|
115878
|
+
}
|
|
115879
|
+
}
|
|
115880
|
+
};
|
|
115881
|
+
Niivue.prototype.drawMosaic = function(mosaicStr) {
|
|
115882
|
+
if (this.volumes.length === 0) {
|
|
115883
|
+
console.log("Unable to draw mosaic until voxel-based image is loaded");
|
|
115884
|
+
return;
|
|
115885
|
+
}
|
|
115886
|
+
this.screenSlices = [];
|
|
115887
|
+
let fovRenderMM = this.screenFieldOfViewMM(this.sliceTypeAxial, true);
|
|
115888
|
+
let fovSliceMM = this.screenFieldOfViewMM(this.sliceTypeAxial);
|
|
115889
|
+
mosaicStr = mosaicStr.replaceAll(";", " ;").trim();
|
|
115890
|
+
let axiMM = [];
|
|
115891
|
+
let corMM = [];
|
|
115892
|
+
let sagMM = [];
|
|
115893
|
+
let items = mosaicStr.split(/\s+/);
|
|
115894
|
+
let scale2 = 1;
|
|
115895
|
+
let labelSize = this.opts.textHeight;
|
|
115896
|
+
for (let pass = 0; pass < 2; pass++) {
|
|
115897
|
+
let isRender = false;
|
|
115898
|
+
let isCrossLines = false;
|
|
115899
|
+
isRender = false;
|
|
115900
|
+
let rowHt = 0;
|
|
115901
|
+
let left = 0;
|
|
115902
|
+
let top = 0;
|
|
115903
|
+
let mxRowWid = 0;
|
|
115904
|
+
let isLabel = false;
|
|
115905
|
+
let axCorSag = this.sliceTypeAxial;
|
|
115906
|
+
for (let i2 = 0; i2 < items.length; i2++) {
|
|
115907
|
+
let item = items[i2];
|
|
115908
|
+
if (item.includes("X")) {
|
|
115909
|
+
isCrossLines = true;
|
|
115910
|
+
continue;
|
|
115911
|
+
}
|
|
115912
|
+
if (item.includes("L")) {
|
|
115913
|
+
isLabel = !item.includes("-");
|
|
115914
|
+
continue;
|
|
115915
|
+
}
|
|
115916
|
+
if (item.includes("V") || item.includes("H")) {
|
|
115917
|
+
i2++;
|
|
115918
|
+
continue;
|
|
115919
|
+
}
|
|
115920
|
+
if (item.includes("A"))
|
|
115921
|
+
axCorSag = this.sliceTypeAxial;
|
|
115922
|
+
if (item.includes("C"))
|
|
115923
|
+
axCorSag = this.sliceTypeCoronal;
|
|
115924
|
+
if (item.includes("S"))
|
|
115925
|
+
axCorSag = this.sliceTypeSagittal;
|
|
115926
|
+
if (item.includes("R"))
|
|
115927
|
+
isRender = true;
|
|
115928
|
+
if (item.includes(";")) {
|
|
115929
|
+
top += rowHt;
|
|
115930
|
+
mxRowWid = Math.max(mxRowWid, left);
|
|
115931
|
+
rowHt = 0;
|
|
115932
|
+
left = 0;
|
|
115933
|
+
}
|
|
115934
|
+
let sliceMM = parseFloat(item, NaN);
|
|
115935
|
+
if (isNaN(sliceMM))
|
|
115936
|
+
continue;
|
|
115937
|
+
let w = 0;
|
|
115938
|
+
let h = 0;
|
|
115939
|
+
let fov = fovSliceMM;
|
|
115940
|
+
if (isRender)
|
|
115941
|
+
fov = fovRenderMM;
|
|
115942
|
+
if (axCorSag === this.sliceTypeSagittal)
|
|
115943
|
+
w = fov[1];
|
|
115944
|
+
else
|
|
115945
|
+
w = fov[0];
|
|
115946
|
+
if (axCorSag === this.sliceTypeAxial)
|
|
115947
|
+
h = fov[1];
|
|
115948
|
+
else
|
|
115949
|
+
h = fov[2];
|
|
115950
|
+
if (pass === 0) {
|
|
115951
|
+
if (!isRender) {
|
|
115952
|
+
if (axCorSag === this.sliceTypeAxial)
|
|
115953
|
+
axiMM.push(sliceMM);
|
|
115954
|
+
if (axCorSag === this.sliceTypeCoronal)
|
|
115955
|
+
corMM.push(sliceMM);
|
|
115956
|
+
if (axCorSag === this.sliceTypeSagittal)
|
|
115957
|
+
sagMM.push(sliceMM);
|
|
115958
|
+
}
|
|
115959
|
+
} else {
|
|
115960
|
+
let ltwh = [scale2 * left, scale2 * top, scale2 * w, scale2 * h];
|
|
115961
|
+
this.opts.textHeight = isLabel ? labelSize : 0;
|
|
115962
|
+
if (isRender) {
|
|
115963
|
+
let inf = sliceMM < 0 ? -Infinity : Infinity;
|
|
115964
|
+
if (Object.is(sliceMM, -0))
|
|
115965
|
+
inf = -Infinity;
|
|
115966
|
+
this.draw2DMM(ltwh, axCorSag, inf, isLabel);
|
|
115967
|
+
} else
|
|
115968
|
+
this.draw2D(ltwh, axCorSag, sliceMM, isLabel);
|
|
115969
|
+
if (isCrossLines) {
|
|
115970
|
+
this.drawCrossLines(this.screenSlices.length - 1, axCorSag, axiMM, corMM, sagMM);
|
|
115971
|
+
}
|
|
115972
|
+
isRender = false;
|
|
115973
|
+
isCrossLines = false;
|
|
115974
|
+
}
|
|
115975
|
+
left += w;
|
|
115976
|
+
rowHt = Math.max(rowHt, h);
|
|
115977
|
+
}
|
|
115978
|
+
top += rowHt;
|
|
115979
|
+
mxRowWid = Math.max(mxRowWid, left);
|
|
115980
|
+
if (mxRowWid <= 0 || top <= 0)
|
|
115981
|
+
break;
|
|
115982
|
+
let scaleW = this.gl.canvas.width / mxRowWid;
|
|
115983
|
+
let scaleH = this.effectiveCanvasHeight() / top;
|
|
115984
|
+
scale2 = Math.min(scaleW, scaleH);
|
|
115985
|
+
}
|
|
115986
|
+
this.opts.textHeight = labelSize;
|
|
115987
|
+
};
|
|
113741
115988
|
Niivue.prototype.drawScene = function() {
|
|
113742
115989
|
if (!this.initialized) {
|
|
113743
115990
|
return;
|
|
113744
115991
|
}
|
|
115992
|
+
this.colorbarHeight = 0;
|
|
113745
115993
|
this.gl.clearColor(this.opts.backColor[0], this.opts.backColor[1], this.opts.backColor[2], this.opts.backColor[3]);
|
|
113746
|
-
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
|
|
115994
|
+
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
|
|
113747
115995
|
if (this.bmpTexture) {
|
|
113748
115996
|
this.drawThumbnail();
|
|
113749
115997
|
return;
|
|
@@ -113751,6 +115999,7 @@ Niivue.prototype.drawScene = function() {
|
|
|
113751
115999
|
let posString = "";
|
|
113752
116000
|
if (this.volumes.length === 0 || typeof this.volumes[0].dims === "undefined") {
|
|
113753
116001
|
if (this.meshes.length > 0) {
|
|
116002
|
+
this.screenSlices = [];
|
|
113754
116003
|
this.sliceType = this.sliceTypeRender;
|
|
113755
116004
|
return this.draw3D();
|
|
113756
116005
|
}
|
|
@@ -113759,64 +116008,91 @@ Niivue.prototype.drawScene = function() {
|
|
|
113759
116008
|
}
|
|
113760
116009
|
if (!this.back.hasOwnProperty("dims"))
|
|
113761
116010
|
return;
|
|
113762
|
-
if (this.
|
|
113763
|
-
|
|
113764
|
-
|
|
113765
|
-
|
|
113766
|
-
|
|
113767
|
-
|
|
113768
|
-
|
|
113769
|
-
|
|
113770
|
-
|
|
113771
|
-
|
|
113772
|
-
|
|
113773
|
-
|
|
113774
|
-
|
|
113775
|
-
|
|
113776
|
-
|
|
113777
|
-
|
|
113778
|
-
|
|
113779
|
-
|
|
113780
|
-
|
|
113781
|
-
|
|
113782
|
-
this.
|
|
113783
|
-
|
|
113784
|
-
|
|
113785
|
-
this.draw2D(leftTopWidthHeight, 1);
|
|
113786
|
-
} else if (this.sliceType === this.sliceTypeSagittal) {
|
|
113787
|
-
let leftTopWidthHeight = this.scaleSlice(volScale[1], volScale[2]);
|
|
113788
|
-
this.draw2D(leftTopWidthHeight, 2);
|
|
116011
|
+
if (this.isDragging && this.scene.clipPlaneDepthAziElev[0] < 1.8 && this.inRenderTile(this.dragStart[0], this.dragStart[1]) >= 0) {
|
|
116012
|
+
let x2 = this.dragStart[0] - this.dragEnd[0];
|
|
116013
|
+
let y = this.dragStart[1] - this.dragEnd[1];
|
|
116014
|
+
let depthAziElev = this.dragClipPlaneStartDepthAziElev.slice();
|
|
116015
|
+
depthAziElev[1] -= x2;
|
|
116016
|
+
depthAziElev[1] = depthAziElev[1] % 360;
|
|
116017
|
+
depthAziElev[2] += y;
|
|
116018
|
+
if (depthAziElev[1] !== this.scene.clipPlaneDepthAziElev[1] || depthAziElev[2] !== this.scene.clipPlaneDepthAziElev[2]) {
|
|
116019
|
+
this.scene.clipPlaneDepthAziElev = depthAziElev;
|
|
116020
|
+
return this.setClipPlane(this.scene.clipPlaneDepthAziElev);
|
|
116021
|
+
}
|
|
116022
|
+
}
|
|
116023
|
+
if (this.sliceMosaicString.length < 1 && this.sliceType === this.sliceTypeRender) {
|
|
116024
|
+
this.screenSlices = [];
|
|
116025
|
+
this.draw3D();
|
|
116026
|
+
if (this.opts.isColorbar)
|
|
116027
|
+
this.drawColorbar();
|
|
116028
|
+
return;
|
|
116029
|
+
}
|
|
116030
|
+
if (this.opts.isColorbar)
|
|
116031
|
+
this.reserveColorbarPanel();
|
|
116032
|
+
if (this.sliceMosaicString.length > 0) {
|
|
116033
|
+
this.drawMosaic(this.sliceMosaicString);
|
|
113789
116034
|
} else {
|
|
113790
|
-
|
|
113791
|
-
|
|
113792
|
-
|
|
113793
|
-
|
|
113794
|
-
if (
|
|
113795
|
-
|
|
113796
|
-
|
|
113797
|
-
|
|
113798
|
-
this.draw2D([ltwh3x1[0], ltwh3x1[1], wX1, hY1], 0);
|
|
113799
|
-
this.draw2D([ltwh3x1[0] + wX1, ltwh3x1[1], wX1, hZ1], 1);
|
|
113800
|
-
this.draw2D([ltwh3x1[0] + wX1 + wX1, ltwh3x1[1], hY1, hZ1], 2);
|
|
116035
|
+
this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
|
|
116036
|
+
this.screenSlices = [];
|
|
116037
|
+
if (this.sliceType === this.sliceTypeAxial) {
|
|
116038
|
+
this.draw2D([0, 0, 0, 0], 0);
|
|
116039
|
+
} else if (this.sliceType === this.sliceTypeCoronal) {
|
|
116040
|
+
this.draw2D([0, 0, 0, 0], 1);
|
|
116041
|
+
} else if (this.sliceType === this.sliceTypeSagittal) {
|
|
116042
|
+
this.draw2D([0, 0, 0, 0], 2);
|
|
113801
116043
|
} else {
|
|
113802
|
-
let
|
|
113803
|
-
let
|
|
113804
|
-
let
|
|
113805
|
-
|
|
113806
|
-
this.
|
|
113807
|
-
|
|
113808
|
-
|
|
113809
|
-
|
|
113810
|
-
|
|
113811
|
-
|
|
113812
|
-
|
|
113813
|
-
|
|
113814
|
-
|
|
113815
|
-
|
|
116044
|
+
let { volScale, vox, longestAxis } = this.sliceScale();
|
|
116045
|
+
let pad = this.opts.multiplanarPadPixels;
|
|
116046
|
+
let ltwh = this.scaleSlice(volScale[0] + volScale[1], volScale[1] + volScale[2], pad * 1, pad * 1);
|
|
116047
|
+
let wX = ltwh[2] * volScale[0] / (volScale[0] + volScale[1]);
|
|
116048
|
+
let ltwh3x1 = this.scaleSlice(volScale[0] + volScale[0] + volScale[1], Math.max(volScale[1], volScale[2]), pad * 2);
|
|
116049
|
+
let mx = Math.max(Math.max(volScale[1], volScale[2]), volScale[0]);
|
|
116050
|
+
let ltwh4x1 = this.scaleSlice(volScale[0] + volScale[0] + volScale[1] + mx, mx, pad * 3);
|
|
116051
|
+
let wX1 = ltwh3x1[2] * volScale[0] / (volScale[0] + volScale[0] + volScale[1]);
|
|
116052
|
+
if (wX1 > wX && !this.graph.autoSizeMultiplanar) {
|
|
116053
|
+
let pixScale = wX1 / volScale[0];
|
|
116054
|
+
let hY1 = volScale[1] * pixScale;
|
|
116055
|
+
let hZ1 = volScale[2] * pixScale;
|
|
116056
|
+
if (ltwh3x1[3] === ltwh4x1[3]) {
|
|
116057
|
+
ltwh3x1 = ltwh4x1;
|
|
116058
|
+
this.draw3D([
|
|
116059
|
+
ltwh3x1[0] + wX1 + wX1 + hY1 + pad * 3,
|
|
116060
|
+
ltwh3x1[1],
|
|
116061
|
+
ltwh4x1[3],
|
|
116062
|
+
ltwh4x1[3]
|
|
116063
|
+
]);
|
|
116064
|
+
}
|
|
116065
|
+
this.draw2D([ltwh3x1[0], ltwh3x1[1], wX1, hY1], 0);
|
|
116066
|
+
this.draw2D([ltwh3x1[0] + wX1 + pad, ltwh3x1[1], wX1, hZ1], 1);
|
|
116067
|
+
this.draw2D([ltwh3x1[0] + wX1 + wX1 + pad * 2, ltwh3x1[1], hY1, hZ1], 2);
|
|
116068
|
+
} else {
|
|
116069
|
+
let wY = ltwh[2] - wX;
|
|
116070
|
+
let hY = ltwh[3] * volScale[1] / (volScale[1] + volScale[2]);
|
|
116071
|
+
let hZ = ltwh[3] - hY;
|
|
116072
|
+
this.draw2D([ltwh[0], ltwh[1] + hZ + pad, wX, hY], 0);
|
|
116073
|
+
this.draw2D([ltwh[0], ltwh[1], wX, hZ], 1);
|
|
116074
|
+
this.draw2D([ltwh[0] + wX + pad, ltwh[1], wY, hZ], 2);
|
|
116075
|
+
if (!this.graph.autoSizeMultiplanar)
|
|
116076
|
+
this.draw3D([ltwh[0] + wX + pad, ltwh[1] + hZ + pad, wY, hY]);
|
|
113816
116077
|
}
|
|
113817
116078
|
}
|
|
113818
116079
|
}
|
|
113819
|
-
if (this.
|
|
116080
|
+
if (this.opts.isRuler)
|
|
116081
|
+
this.drawRuler();
|
|
116082
|
+
if (this.opts.isColorbar)
|
|
116083
|
+
this.drawColorbar();
|
|
116084
|
+
if (this.isDragging) {
|
|
116085
|
+
if (this.opts.isDragShowsMeasurementTool) {
|
|
116086
|
+
this.drawMeasurementTool([
|
|
116087
|
+
this.dragStart[0],
|
|
116088
|
+
this.dragStart[1],
|
|
116089
|
+
this.dragEnd[0],
|
|
116090
|
+
this.dragEnd[1]
|
|
116091
|
+
]);
|
|
116092
|
+
return;
|
|
116093
|
+
}
|
|
116094
|
+
if (this.inRenderTile(this.dragStart[0], this.dragStart[1]) >= 0)
|
|
116095
|
+
return;
|
|
113820
116096
|
let width = Math.abs(this.dragStart[0] - this.dragEnd[0]);
|
|
113821
116097
|
let height = Math.abs(this.dragStart[1] - this.dragEnd[1]);
|
|
113822
116098
|
this.drawSelectionBox([
|