copper3d 3.4.7 → 3.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Loader/copperDicomLoader.d.ts +7 -1
- package/dist/Loader/copperDicomLoader.js +45 -0
- package/dist/Loader/copperDicomLoader.js.map +1 -1
- package/dist/Renderer/copperRenderer.d.ts +2 -0
- package/dist/Renderer/copperRenderer.js +7 -0
- package/dist/Renderer/copperRenderer.js.map +1 -1
- package/dist/Scene/copperScene.d.ts +8 -3
- package/dist/Scene/copperScene.js +173 -34
- package/dist/Scene/copperScene.js.map +1 -1
- package/dist/Utils/segmentation/NrrdTools.js +6 -0
- package/dist/Utils/segmentation/NrrdTools.js.map +1 -1
- package/dist/Utils/texture2d.d.ts +8 -6
- package/dist/Utils/texture2d.js +30 -37
- package/dist/Utils/texture2d.js.map +1 -1
- package/dist/bundle.esm.js +261 -549
- package/dist/bundle.umd.js +261 -549
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types/Loader/copperDicomLoader.d.ts +7 -1
- package/dist/types/Renderer/copperRenderer.d.ts +2 -0
- package/dist/types/Scene/copperScene.d.ts +8 -3
- package/dist/types/Utils/texture2d.d.ts +8 -6
- package/dist/types/index.d.ts +3 -3
- package/dist/types/types/types.d.ts +43 -1
- package/dist/types/types.d.ts +43 -1
- package/package.json +1 -1
package/dist/bundle.esm.js
CHANGED
|
@@ -40826,6 +40826,17 @@ function copperDicomLoader(url, callback) {
|
|
|
40826
40826
|
windowCenter = 226;
|
|
40827
40827
|
windowWidth = 537;
|
|
40828
40828
|
}
|
|
40829
|
+
// Spatial geometry tags (DS VR, backslash-delimited) used to place the image plane
|
|
40830
|
+
// at its true world pose, plus InstanceNumber as an ordering fallback.
|
|
40831
|
+
const ipp = parseDS(dataSet.string("x00200032")); // ImagePositionPatient
|
|
40832
|
+
const iop = parseDS(dataSet.string("x00200037")); // ImageOrientationPatient
|
|
40833
|
+
const spacing = parseDS(dataSet.string("x00280030")); // PixelSpacing
|
|
40834
|
+
let instanceNumber = parseInt(dataSet.string("x00200013"));
|
|
40835
|
+
if (Number.isNaN(instanceNumber))
|
|
40836
|
+
instanceNumber = undefined;
|
|
40837
|
+
const corners = ipp && iop && spacing
|
|
40838
|
+
? computeImagePlaneCorners(ipp, iop, spacing, w, h)
|
|
40839
|
+
: undefined;
|
|
40829
40840
|
let pixelData = dataSet.elements.x7fe00010;
|
|
40830
40841
|
let uint16 = convertImplicitElement(pixelData, dicomFileAsBuffer);
|
|
40831
40842
|
let voiLUT;
|
|
@@ -40844,10 +40855,44 @@ function copperDicomLoader(url, callback) {
|
|
|
40844
40855
|
uint16,
|
|
40845
40856
|
uint8,
|
|
40846
40857
|
order,
|
|
40858
|
+
instanceNumber,
|
|
40859
|
+
imagePositionPatient: ipp,
|
|
40860
|
+
imageOrientationPatient: iop,
|
|
40861
|
+
pixelSpacing: spacing,
|
|
40862
|
+
corners,
|
|
40847
40863
|
};
|
|
40848
40864
|
callback && callback(copperVolume);
|
|
40849
40865
|
});
|
|
40850
40866
|
}
|
|
40867
|
+
// Parse a DICOM DS/IS multi-value string ("a\\b\\c") into a number[].
|
|
40868
|
+
function parseDS(s) {
|
|
40869
|
+
if (!s)
|
|
40870
|
+
return undefined;
|
|
40871
|
+
const a = s.split("\\").map((v) => parseFloat(v));
|
|
40872
|
+
return a.length && !a.some((n) => Number.isNaN(n)) ? a : undefined;
|
|
40873
|
+
}
|
|
40874
|
+
/**
|
|
40875
|
+
* Compute the 4 world-space corners of a DICOM image plane from its geometry tags.
|
|
40876
|
+
* IPP is the CENTER of pixel (0,0), so corners sit half a pixel out. PixelSpacing is
|
|
40877
|
+
* [rowSpacing, colSpacing]; IOP's first vector is the +column-index direction.
|
|
40878
|
+
*/
|
|
40879
|
+
function computeImagePlaneCorners(ipp, iop, spacing, cols, rows) {
|
|
40880
|
+
const row = new Vector3(iop[0], iop[1], iop[2]); // +column-index dir
|
|
40881
|
+
const col = new Vector3(iop[3], iop[4], iop[5]); // +row-index dir
|
|
40882
|
+
const sc = spacing[1]; // column spacing (along row)
|
|
40883
|
+
const sr = spacing[0]; // row spacing (along col)
|
|
40884
|
+
const O = new Vector3(ipp[0], ipp[1], ipp[2]); // center of pixel (0,0)
|
|
40885
|
+
const p = (a, b) => O.clone()
|
|
40886
|
+
.addScaledVector(row, a * sc)
|
|
40887
|
+
.addScaledVector(col, b * sr)
|
|
40888
|
+
.toArray();
|
|
40889
|
+
return {
|
|
40890
|
+
tl: p(-0.5, -0.5),
|
|
40891
|
+
tr: p(cols - 0.5, -0.5),
|
|
40892
|
+
bl: p(-0.5, rows - 0.5),
|
|
40893
|
+
br: p(cols - 0.5, rows - 0.5),
|
|
40894
|
+
};
|
|
40895
|
+
}
|
|
40851
40896
|
function convertImplicitElement(elementData, dicomFileAsBuffer) {
|
|
40852
40897
|
let w1Buffer = dicomParser.sharedCopy(dicomFileAsBuffer, elementData.dataOffset, elementData.length);
|
|
40853
40898
|
let unit16 = new Uint16Array(w1Buffer.buffer, w1Buffer.byteOffset, w1Buffer.byteLength / Uint16Array.BYTES_PER_ELEMENT);
|
|
@@ -40884,514 +40929,23 @@ function getLut(data, windowWidth, windowCenter, invert, voiLUT) {
|
|
|
40884
40929
|
};
|
|
40885
40930
|
}
|
|
40886
40931
|
|
|
40887
|
-
|
|
40932
|
+
var vert_2d = "#define GLSLIFY 1\nout vec2 vUv;void main(){gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);vUv=vec2(uv.x,1.0-uv.y);}"; // eslint-disable-line
|
|
40888
40933
|
|
|
40889
|
-
|
|
40890
|
-
var u8$1 = Uint8Array, u16$1 = Uint16Array, u32 = Uint32Array;
|
|
40891
|
-
// fixed length extra bits
|
|
40892
|
-
var fleb$1 = new u8$1([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, /* unused */ 0, 0, /* impossible */ 0]);
|
|
40893
|
-
// fixed distance extra bits
|
|
40894
|
-
// see fleb note
|
|
40895
|
-
var fdeb$1 = new u8$1([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, /* unused */ 0, 0]);
|
|
40896
|
-
// code length index map
|
|
40897
|
-
var clim$1 = new u8$1([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
|
|
40898
|
-
// get base, reverse index map from extra bits
|
|
40899
|
-
var freb$1 = function (eb, start) {
|
|
40900
|
-
var b = new u16$1(31);
|
|
40901
|
-
for (var i = 0; i < 31; ++i) {
|
|
40902
|
-
b[i] = start += 1 << eb[i - 1];
|
|
40903
|
-
}
|
|
40904
|
-
// numbers here are at max 18 bits
|
|
40905
|
-
var r = new u32(b[30]);
|
|
40906
|
-
for (var i = 1; i < 30; ++i) {
|
|
40907
|
-
for (var j = b[i]; j < b[i + 1]; ++j) {
|
|
40908
|
-
r[j] = ((j - b[i]) << 5) | i;
|
|
40909
|
-
}
|
|
40910
|
-
}
|
|
40911
|
-
return [b, r];
|
|
40912
|
-
};
|
|
40913
|
-
var _a$1 = freb$1(fleb$1, 2), fl$1 = _a$1[0], revfl$1 = _a$1[1];
|
|
40914
|
-
// we can ignore the fact that the other numbers are wrong; they never happen anyway
|
|
40915
|
-
fl$1[28] = 258, revfl$1[258] = 28;
|
|
40916
|
-
var _b$1 = freb$1(fdeb$1, 0), fd$1 = _b$1[0];
|
|
40917
|
-
// map of value to reverse (assuming 16 bits)
|
|
40918
|
-
var rev$1 = new u16$1(32768);
|
|
40919
|
-
for (var i$1 = 0; i$1 < 32768; ++i$1) {
|
|
40920
|
-
// reverse table algorithm from SO
|
|
40921
|
-
var x$1 = ((i$1 & 0xAAAA) >>> 1) | ((i$1 & 0x5555) << 1);
|
|
40922
|
-
x$1 = ((x$1 & 0xCCCC) >>> 2) | ((x$1 & 0x3333) << 2);
|
|
40923
|
-
x$1 = ((x$1 & 0xF0F0) >>> 4) | ((x$1 & 0x0F0F) << 4);
|
|
40924
|
-
rev$1[i$1] = (((x$1 & 0xFF00) >>> 8) | ((x$1 & 0x00FF) << 8)) >>> 1;
|
|
40925
|
-
}
|
|
40926
|
-
// create huffman tree from u8 "map": index -> code length for code index
|
|
40927
|
-
// mb (max bits) must be at most 15
|
|
40928
|
-
// TODO: optimize/split up?
|
|
40929
|
-
var hMap$1 = (function (cd, mb, r) {
|
|
40930
|
-
var s = cd.length;
|
|
40931
|
-
// index
|
|
40932
|
-
var i = 0;
|
|
40933
|
-
// u16 "map": index -> # of codes with bit length = index
|
|
40934
|
-
var l = new u16$1(mb);
|
|
40935
|
-
// length of cd must be 288 (total # of codes)
|
|
40936
|
-
for (; i < s; ++i) {
|
|
40937
|
-
if (cd[i])
|
|
40938
|
-
++l[cd[i] - 1];
|
|
40939
|
-
}
|
|
40940
|
-
// u16 "map": index -> minimum code for bit length = index
|
|
40941
|
-
var le = new u16$1(mb);
|
|
40942
|
-
for (i = 0; i < mb; ++i) {
|
|
40943
|
-
le[i] = (le[i - 1] + l[i - 1]) << 1;
|
|
40944
|
-
}
|
|
40945
|
-
var co;
|
|
40946
|
-
if (r) {
|
|
40947
|
-
// u16 "map": index -> number of actual bits, symbol for code
|
|
40948
|
-
co = new u16$1(1 << mb);
|
|
40949
|
-
// bits to remove for reverser
|
|
40950
|
-
var rvb = 15 - mb;
|
|
40951
|
-
for (i = 0; i < s; ++i) {
|
|
40952
|
-
// ignore 0 lengths
|
|
40953
|
-
if (cd[i]) {
|
|
40954
|
-
// num encoding both symbol and bits read
|
|
40955
|
-
var sv = (i << 4) | cd[i];
|
|
40956
|
-
// free bits
|
|
40957
|
-
var r_1 = mb - cd[i];
|
|
40958
|
-
// start value
|
|
40959
|
-
var v = le[cd[i] - 1]++ << r_1;
|
|
40960
|
-
// m is end value
|
|
40961
|
-
for (var m = v | ((1 << r_1) - 1); v <= m; ++v) {
|
|
40962
|
-
// every 16 bit value starting with the code yields the same result
|
|
40963
|
-
co[rev$1[v] >>> rvb] = sv;
|
|
40964
|
-
}
|
|
40965
|
-
}
|
|
40966
|
-
}
|
|
40967
|
-
}
|
|
40968
|
-
else {
|
|
40969
|
-
co = new u16$1(s);
|
|
40970
|
-
for (i = 0; i < s; ++i) {
|
|
40971
|
-
if (cd[i]) {
|
|
40972
|
-
co[i] = rev$1[le[cd[i] - 1]++] >>> (15 - cd[i]);
|
|
40973
|
-
}
|
|
40974
|
-
}
|
|
40975
|
-
}
|
|
40976
|
-
return co;
|
|
40977
|
-
});
|
|
40978
|
-
// fixed length tree
|
|
40979
|
-
var flt$1 = new u8$1(288);
|
|
40980
|
-
for (var i$1 = 0; i$1 < 144; ++i$1)
|
|
40981
|
-
flt$1[i$1] = 8;
|
|
40982
|
-
for (var i$1 = 144; i$1 < 256; ++i$1)
|
|
40983
|
-
flt$1[i$1] = 9;
|
|
40984
|
-
for (var i$1 = 256; i$1 < 280; ++i$1)
|
|
40985
|
-
flt$1[i$1] = 7;
|
|
40986
|
-
for (var i$1 = 280; i$1 < 288; ++i$1)
|
|
40987
|
-
flt$1[i$1] = 8;
|
|
40988
|
-
// fixed distance tree
|
|
40989
|
-
var fdt$1 = new u8$1(32);
|
|
40990
|
-
for (var i$1 = 0; i$1 < 32; ++i$1)
|
|
40991
|
-
fdt$1[i$1] = 5;
|
|
40992
|
-
// fixed length map
|
|
40993
|
-
var flrm$1 = /*#__PURE__*/ hMap$1(flt$1, 9, 1);
|
|
40994
|
-
// fixed distance map
|
|
40995
|
-
var fdrm$1 = /*#__PURE__*/ hMap$1(fdt$1, 5, 1);
|
|
40996
|
-
// find max of array
|
|
40997
|
-
var max$1 = function (a) {
|
|
40998
|
-
var m = a[0];
|
|
40999
|
-
for (var i = 1; i < a.length; ++i) {
|
|
41000
|
-
if (a[i] > m)
|
|
41001
|
-
m = a[i];
|
|
41002
|
-
}
|
|
41003
|
-
return m;
|
|
41004
|
-
};
|
|
41005
|
-
// read d, starting at bit p and mask with m
|
|
41006
|
-
var bits$1 = function (d, p, m) {
|
|
41007
|
-
var o = (p / 8) | 0;
|
|
41008
|
-
return ((d[o] | (d[o + 1] << 8)) >> (p & 7)) & m;
|
|
41009
|
-
};
|
|
41010
|
-
// read d, starting at bit p continuing for at least 16 bits
|
|
41011
|
-
var bits16$1 = function (d, p) {
|
|
41012
|
-
var o = (p / 8) | 0;
|
|
41013
|
-
return ((d[o] | (d[o + 1] << 8) | (d[o + 2] << 16)) >> (p & 7));
|
|
41014
|
-
};
|
|
41015
|
-
// get end of byte
|
|
41016
|
-
var shft$1 = function (p) { return ((p + 7) / 8) | 0; };
|
|
41017
|
-
// typed array slice - allows garbage collector to free original reference,
|
|
41018
|
-
// while being more compatible than .slice
|
|
41019
|
-
var slc$1 = function (v, s, e) {
|
|
41020
|
-
if (s == null || s < 0)
|
|
41021
|
-
s = 0;
|
|
41022
|
-
if (e == null || e > v.length)
|
|
41023
|
-
e = v.length;
|
|
41024
|
-
// can't use .constructor in case user-supplied
|
|
41025
|
-
var n = new (v.BYTES_PER_ELEMENT == 2 ? u16$1 : v.BYTES_PER_ELEMENT == 4 ? u32 : u8$1)(e - s);
|
|
41026
|
-
n.set(v.subarray(s, e));
|
|
41027
|
-
return n;
|
|
41028
|
-
};
|
|
41029
|
-
// error codes
|
|
41030
|
-
var ec$1 = [
|
|
41031
|
-
'unexpected EOF',
|
|
41032
|
-
'invalid block type',
|
|
41033
|
-
'invalid length/literal',
|
|
41034
|
-
'invalid distance',
|
|
41035
|
-
'stream finished',
|
|
41036
|
-
'no stream handler',
|
|
41037
|
-
,
|
|
41038
|
-
'no callback',
|
|
41039
|
-
'invalid UTF-8 data',
|
|
41040
|
-
'extra field too long',
|
|
41041
|
-
'date not in range 1980-2099',
|
|
41042
|
-
'filename too long',
|
|
41043
|
-
'stream finishing',
|
|
41044
|
-
'invalid zip data'
|
|
41045
|
-
// determined by unknown compression method
|
|
41046
|
-
];
|
|
41047
|
-
var err$1 = function (ind, msg, nt) {
|
|
41048
|
-
var e = new Error(msg || ec$1[ind]);
|
|
41049
|
-
e.code = ind;
|
|
41050
|
-
if (Error.captureStackTrace)
|
|
41051
|
-
Error.captureStackTrace(e, err$1);
|
|
41052
|
-
if (!nt)
|
|
41053
|
-
throw e;
|
|
41054
|
-
return e;
|
|
41055
|
-
};
|
|
41056
|
-
// expands raw DEFLATE data
|
|
41057
|
-
var inflt$1 = function (dat, buf, st) {
|
|
41058
|
-
// source length
|
|
41059
|
-
var sl = dat.length;
|
|
41060
|
-
if (!sl || (st && st.f && !st.l))
|
|
41061
|
-
return buf || new u8$1(0);
|
|
41062
|
-
// have to estimate size
|
|
41063
|
-
var noBuf = !buf || st;
|
|
41064
|
-
// no state
|
|
41065
|
-
var noSt = !st || st.i;
|
|
41066
|
-
if (!st)
|
|
41067
|
-
st = {};
|
|
41068
|
-
// Assumes roughly 33% compression ratio average
|
|
41069
|
-
if (!buf)
|
|
41070
|
-
buf = new u8$1(sl * 3);
|
|
41071
|
-
// ensure buffer can fit at least l elements
|
|
41072
|
-
var cbuf = function (l) {
|
|
41073
|
-
var bl = buf.length;
|
|
41074
|
-
// need to increase size to fit
|
|
41075
|
-
if (l > bl) {
|
|
41076
|
-
// Double or set to necessary, whichever is greater
|
|
41077
|
-
var nbuf = new u8$1(Math.max(bl * 2, l));
|
|
41078
|
-
nbuf.set(buf);
|
|
41079
|
-
buf = nbuf;
|
|
41080
|
-
}
|
|
41081
|
-
};
|
|
41082
|
-
// last chunk bitpos bytes
|
|
41083
|
-
var final = st.f || 0, pos = st.p || 0, bt = st.b || 0, lm = st.l, dm = st.d, lbt = st.m, dbt = st.n;
|
|
41084
|
-
// total bits
|
|
41085
|
-
var tbts = sl * 8;
|
|
41086
|
-
do {
|
|
41087
|
-
if (!lm) {
|
|
41088
|
-
// BFINAL - this is only 1 when last chunk is next
|
|
41089
|
-
final = bits$1(dat, pos, 1);
|
|
41090
|
-
// type: 0 = no compression, 1 = fixed huffman, 2 = dynamic huffman
|
|
41091
|
-
var type = bits$1(dat, pos + 1, 3);
|
|
41092
|
-
pos += 3;
|
|
41093
|
-
if (!type) {
|
|
41094
|
-
// go to end of byte boundary
|
|
41095
|
-
var s = shft$1(pos) + 4, l = dat[s - 4] | (dat[s - 3] << 8), t = s + l;
|
|
41096
|
-
if (t > sl) {
|
|
41097
|
-
if (noSt)
|
|
41098
|
-
err$1(0);
|
|
41099
|
-
break;
|
|
41100
|
-
}
|
|
41101
|
-
// ensure size
|
|
41102
|
-
if (noBuf)
|
|
41103
|
-
cbuf(bt + l);
|
|
41104
|
-
// Copy over uncompressed data
|
|
41105
|
-
buf.set(dat.subarray(s, t), bt);
|
|
41106
|
-
// Get new bitpos, update byte count
|
|
41107
|
-
st.b = bt += l, st.p = pos = t * 8, st.f = final;
|
|
41108
|
-
continue;
|
|
41109
|
-
}
|
|
41110
|
-
else if (type == 1)
|
|
41111
|
-
lm = flrm$1, dm = fdrm$1, lbt = 9, dbt = 5;
|
|
41112
|
-
else if (type == 2) {
|
|
41113
|
-
// literal lengths
|
|
41114
|
-
var hLit = bits$1(dat, pos, 31) + 257, hcLen = bits$1(dat, pos + 10, 15) + 4;
|
|
41115
|
-
var tl = hLit + bits$1(dat, pos + 5, 31) + 1;
|
|
41116
|
-
pos += 14;
|
|
41117
|
-
// length+distance tree
|
|
41118
|
-
var ldt = new u8$1(tl);
|
|
41119
|
-
// code length tree
|
|
41120
|
-
var clt = new u8$1(19);
|
|
41121
|
-
for (var i = 0; i < hcLen; ++i) {
|
|
41122
|
-
// use index map to get real code
|
|
41123
|
-
clt[clim$1[i]] = bits$1(dat, pos + i * 3, 7);
|
|
41124
|
-
}
|
|
41125
|
-
pos += hcLen * 3;
|
|
41126
|
-
// code lengths bits
|
|
41127
|
-
var clb = max$1(clt), clbmsk = (1 << clb) - 1;
|
|
41128
|
-
// code lengths map
|
|
41129
|
-
var clm = hMap$1(clt, clb, 1);
|
|
41130
|
-
for (var i = 0; i < tl;) {
|
|
41131
|
-
var r = clm[bits$1(dat, pos, clbmsk)];
|
|
41132
|
-
// bits read
|
|
41133
|
-
pos += r & 15;
|
|
41134
|
-
// symbol
|
|
41135
|
-
var s = r >>> 4;
|
|
41136
|
-
// code length to copy
|
|
41137
|
-
if (s < 16) {
|
|
41138
|
-
ldt[i++] = s;
|
|
41139
|
-
}
|
|
41140
|
-
else {
|
|
41141
|
-
// copy count
|
|
41142
|
-
var c = 0, n = 0;
|
|
41143
|
-
if (s == 16)
|
|
41144
|
-
n = 3 + bits$1(dat, pos, 3), pos += 2, c = ldt[i - 1];
|
|
41145
|
-
else if (s == 17)
|
|
41146
|
-
n = 3 + bits$1(dat, pos, 7), pos += 3;
|
|
41147
|
-
else if (s == 18)
|
|
41148
|
-
n = 11 + bits$1(dat, pos, 127), pos += 7;
|
|
41149
|
-
while (n--)
|
|
41150
|
-
ldt[i++] = c;
|
|
41151
|
-
}
|
|
41152
|
-
}
|
|
41153
|
-
// length tree distance tree
|
|
41154
|
-
var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);
|
|
41155
|
-
// max length bits
|
|
41156
|
-
lbt = max$1(lt);
|
|
41157
|
-
// max dist bits
|
|
41158
|
-
dbt = max$1(dt);
|
|
41159
|
-
lm = hMap$1(lt, lbt, 1);
|
|
41160
|
-
dm = hMap$1(dt, dbt, 1);
|
|
41161
|
-
}
|
|
41162
|
-
else
|
|
41163
|
-
err$1(1);
|
|
41164
|
-
if (pos > tbts) {
|
|
41165
|
-
if (noSt)
|
|
41166
|
-
err$1(0);
|
|
41167
|
-
break;
|
|
41168
|
-
}
|
|
41169
|
-
}
|
|
41170
|
-
// Make sure the buffer can hold this + the largest possible addition
|
|
41171
|
-
// Maximum chunk size (practically, theoretically infinite) is 2^17;
|
|
41172
|
-
if (noBuf)
|
|
41173
|
-
cbuf(bt + 131072);
|
|
41174
|
-
var lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;
|
|
41175
|
-
var lpos = pos;
|
|
41176
|
-
for (;; lpos = pos) {
|
|
41177
|
-
// bits read, code
|
|
41178
|
-
var c = lm[bits16$1(dat, pos) & lms], sym = c >>> 4;
|
|
41179
|
-
pos += c & 15;
|
|
41180
|
-
if (pos > tbts) {
|
|
41181
|
-
if (noSt)
|
|
41182
|
-
err$1(0);
|
|
41183
|
-
break;
|
|
41184
|
-
}
|
|
41185
|
-
if (!c)
|
|
41186
|
-
err$1(2);
|
|
41187
|
-
if (sym < 256)
|
|
41188
|
-
buf[bt++] = sym;
|
|
41189
|
-
else if (sym == 256) {
|
|
41190
|
-
lpos = pos, lm = null;
|
|
41191
|
-
break;
|
|
41192
|
-
}
|
|
41193
|
-
else {
|
|
41194
|
-
var add = sym - 254;
|
|
41195
|
-
// no extra bits needed if less
|
|
41196
|
-
if (sym > 264) {
|
|
41197
|
-
// index
|
|
41198
|
-
var i = sym - 257, b = fleb$1[i];
|
|
41199
|
-
add = bits$1(dat, pos, (1 << b) - 1) + fl$1[i];
|
|
41200
|
-
pos += b;
|
|
41201
|
-
}
|
|
41202
|
-
// dist
|
|
41203
|
-
var d = dm[bits16$1(dat, pos) & dms], dsym = d >>> 4;
|
|
41204
|
-
if (!d)
|
|
41205
|
-
err$1(3);
|
|
41206
|
-
pos += d & 15;
|
|
41207
|
-
var dt = fd$1[dsym];
|
|
41208
|
-
if (dsym > 3) {
|
|
41209
|
-
var b = fdeb$1[dsym];
|
|
41210
|
-
dt += bits16$1(dat, pos) & ((1 << b) - 1), pos += b;
|
|
41211
|
-
}
|
|
41212
|
-
if (pos > tbts) {
|
|
41213
|
-
if (noSt)
|
|
41214
|
-
err$1(0);
|
|
41215
|
-
break;
|
|
41216
|
-
}
|
|
41217
|
-
if (noBuf)
|
|
41218
|
-
cbuf(bt + 131072);
|
|
41219
|
-
var end = bt + add;
|
|
41220
|
-
for (; bt < end; bt += 4) {
|
|
41221
|
-
buf[bt] = buf[bt - dt];
|
|
41222
|
-
buf[bt + 1] = buf[bt + 1 - dt];
|
|
41223
|
-
buf[bt + 2] = buf[bt + 2 - dt];
|
|
41224
|
-
buf[bt + 3] = buf[bt + 3 - dt];
|
|
41225
|
-
}
|
|
41226
|
-
bt = end;
|
|
41227
|
-
}
|
|
41228
|
-
}
|
|
41229
|
-
st.l = lm, st.p = lpos, st.b = bt, st.f = final;
|
|
41230
|
-
if (lm)
|
|
41231
|
-
final = 1, st.m = lbt, st.d = dm, st.n = dbt;
|
|
41232
|
-
} while (!final);
|
|
41233
|
-
return bt == buf.length ? buf : slc$1(buf, 0, bt);
|
|
41234
|
-
};
|
|
41235
|
-
// empty
|
|
41236
|
-
var et$1 = /*#__PURE__*/ new u8$1(0);
|
|
41237
|
-
// read 2 bytes
|
|
41238
|
-
var b2 = function (d, b) { return d[b] | (d[b + 1] << 8); };
|
|
41239
|
-
// read 4 bytes
|
|
41240
|
-
var b4 = function (d, b) { return (d[b] | (d[b + 1] << 8) | (d[b + 2] << 16) | (d[b + 3] << 24)) >>> 0; };
|
|
41241
|
-
var b8 = function (d, b) { return b4(d, b) + (b4(d, b + 4) * 4294967296); };
|
|
41242
|
-
/**
|
|
41243
|
-
* Expands DEFLATE data with no wrapper
|
|
41244
|
-
* @param data The data to decompress
|
|
41245
|
-
* @param out Where to write the data. Saves memory if you know the decompressed size and provide an output buffer of that length.
|
|
41246
|
-
* @returns The decompressed version of the data
|
|
41247
|
-
*/
|
|
41248
|
-
function inflateSync(data, out) {
|
|
41249
|
-
return inflt$1(data, out);
|
|
41250
|
-
}
|
|
41251
|
-
// text decoder
|
|
41252
|
-
var td$1 = typeof TextDecoder != 'undefined' && /*#__PURE__*/ new TextDecoder();
|
|
41253
|
-
// text decoder stream
|
|
41254
|
-
var tds$1 = 0;
|
|
41255
|
-
try {
|
|
41256
|
-
td$1.decode(et$1, { stream: true });
|
|
41257
|
-
tds$1 = 1;
|
|
41258
|
-
}
|
|
41259
|
-
catch (e) { }
|
|
41260
|
-
// decode UTF8
|
|
41261
|
-
var dutf8 = function (d) {
|
|
41262
|
-
for (var r = '', i = 0;;) {
|
|
41263
|
-
var c = d[i++];
|
|
41264
|
-
var eb = (c > 127) + (c > 223) + (c > 239);
|
|
41265
|
-
if (i + eb > d.length)
|
|
41266
|
-
return [r, slc$1(d, i - 1)];
|
|
41267
|
-
if (!eb)
|
|
41268
|
-
r += String.fromCharCode(c);
|
|
41269
|
-
else if (eb == 3) {
|
|
41270
|
-
c = ((c & 15) << 18 | (d[i++] & 63) << 12 | (d[i++] & 63) << 6 | (d[i++] & 63)) - 65536,
|
|
41271
|
-
r += String.fromCharCode(55296 | (c >> 10), 56320 | (c & 1023));
|
|
41272
|
-
}
|
|
41273
|
-
else if (eb & 1)
|
|
41274
|
-
r += String.fromCharCode((c & 31) << 6 | (d[i++] & 63));
|
|
41275
|
-
else
|
|
41276
|
-
r += String.fromCharCode((c & 15) << 12 | (d[i++] & 63) << 6 | (d[i++] & 63));
|
|
41277
|
-
}
|
|
41278
|
-
};
|
|
41279
|
-
/**
|
|
41280
|
-
* Converts a Uint8Array to a string
|
|
41281
|
-
* @param dat The data to decode to string
|
|
41282
|
-
* @param latin1 Whether or not to interpret the data as Latin-1. This should
|
|
41283
|
-
* not need to be true unless encoding to binary string.
|
|
41284
|
-
* @returns The original UTF-8/Latin-1 string
|
|
41285
|
-
*/
|
|
41286
|
-
function strFromU8(dat, latin1) {
|
|
41287
|
-
if (latin1) {
|
|
41288
|
-
var r = '';
|
|
41289
|
-
for (var i = 0; i < dat.length; i += 16384)
|
|
41290
|
-
r += String.fromCharCode.apply(null, dat.subarray(i, i + 16384));
|
|
41291
|
-
return r;
|
|
41292
|
-
}
|
|
41293
|
-
else if (td$1)
|
|
41294
|
-
return td$1.decode(dat);
|
|
41295
|
-
else {
|
|
41296
|
-
var _a = dutf8(dat), out = _a[0], ext = _a[1];
|
|
41297
|
-
if (ext.length)
|
|
41298
|
-
err$1(8);
|
|
41299
|
-
return out;
|
|
41300
|
-
}
|
|
41301
|
-
}
|
|
41302
|
-
// skip local zip header
|
|
41303
|
-
var slzh = function (d, b) { return b + 30 + b2(d, b + 26) + b2(d, b + 28); };
|
|
41304
|
-
// read zip header
|
|
41305
|
-
var zh = function (d, b, z) {
|
|
41306
|
-
var fnl = b2(d, b + 28), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es = b + 46 + fnl, bs = b4(d, b + 20);
|
|
41307
|
-
var _a = z && bs == 4294967295 ? z64e(d, es) : [bs, b4(d, b + 24), b4(d, b + 42)], sc = _a[0], su = _a[1], off = _a[2];
|
|
41308
|
-
return [b2(d, b + 10), sc, su, fn, es + b2(d, b + 30) + b2(d, b + 32), off];
|
|
41309
|
-
};
|
|
41310
|
-
// read zip64 extra field
|
|
41311
|
-
var z64e = function (d, b) {
|
|
41312
|
-
for (; b2(d, b) != 1; b += 4 + b2(d, b + 2))
|
|
41313
|
-
;
|
|
41314
|
-
return [b8(d, b + 12), b8(d, b + 4), b8(d, b + 20)];
|
|
41315
|
-
};
|
|
41316
|
-
/**
|
|
41317
|
-
* Synchronously decompresses a ZIP archive. Prefer using `unzip` for better
|
|
41318
|
-
* performance with more than one file.
|
|
41319
|
-
* @param data The raw compressed ZIP file
|
|
41320
|
-
* @param opts The ZIP extraction options
|
|
41321
|
-
* @returns The decompressed files
|
|
41322
|
-
*/
|
|
41323
|
-
function unzipSync(data, opts) {
|
|
41324
|
-
var files = {};
|
|
41325
|
-
var e = data.length - 22;
|
|
41326
|
-
for (; b4(data, e) != 0x6054B50; --e) {
|
|
41327
|
-
if (!e || data.length - e > 65558)
|
|
41328
|
-
err$1(13);
|
|
41329
|
-
}
|
|
41330
|
-
var c = b2(data, e + 8);
|
|
41331
|
-
if (!c)
|
|
41332
|
-
return {};
|
|
41333
|
-
var o = b4(data, e + 16);
|
|
41334
|
-
var z = o == 4294967295 || c == 65535;
|
|
41335
|
-
if (z) {
|
|
41336
|
-
var ze = b4(data, e - 12);
|
|
41337
|
-
z = b4(data, ze) == 0x6064B50;
|
|
41338
|
-
if (z) {
|
|
41339
|
-
c = b4(data, ze + 32);
|
|
41340
|
-
o = b4(data, ze + 48);
|
|
41341
|
-
}
|
|
41342
|
-
}
|
|
41343
|
-
var fltr = opts && opts.filter;
|
|
41344
|
-
for (var i = 0; i < c; ++i) {
|
|
41345
|
-
var _a = zh(data, o, z), c_2 = _a[0], sc = _a[1], su = _a[2], fn = _a[3], no = _a[4], off = _a[5], b = slzh(data, off);
|
|
41346
|
-
o = no;
|
|
41347
|
-
if (!fltr || fltr({
|
|
41348
|
-
name: fn,
|
|
41349
|
-
size: sc,
|
|
41350
|
-
originalSize: su,
|
|
41351
|
-
compression: c_2
|
|
41352
|
-
})) {
|
|
41353
|
-
if (!c_2)
|
|
41354
|
-
files[fn] = slc$1(data, b, b + sc);
|
|
41355
|
-
else if (c_2 == 8)
|
|
41356
|
-
files[fn] = inflateSync(data.subarray(b, b + sc), new u8$1(su));
|
|
41357
|
-
else
|
|
41358
|
-
err$1(14, 'unknown compression type ' + c_2);
|
|
41359
|
-
}
|
|
41360
|
-
}
|
|
41361
|
-
return files;
|
|
41362
|
-
}
|
|
41363
|
-
|
|
41364
|
-
var vert_2d = "#define GLSLIFY 1\nuniform vec2 size;out vec2 vUv;void main(){gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);vUv.xy=position.xy/size+0.5;vUv.y=1.0-vUv.y;}"; // eslint-disable-line
|
|
41365
|
-
|
|
41366
|
-
var frag_2d = "precision highp float;precision highp int;precision highp sampler2DArray;\n#define GLSLIFY 1\nuniform sampler2DArray diffuse;in vec2 vUv;uniform int depth;out vec4 outColor;void main(){vec4 color=texture(diffuse,vec3(vUv,depth));outColor=vec4(color.rrr*1.5,1.0);}"; // eslint-disable-line
|
|
40934
|
+
var frag_2d = "precision highp float;precision highp int;precision highp sampler2DArray;\n#define GLSLIFY 1\nuniform sampler2DArray diffuse;in vec2 vUv;uniform int depth;uniform float uOpacity;out vec4 outColor;void main(){vec4 color=texture(diffuse,vec3(vUv,depth));outColor=vec4(color.rrr*1.5,uOpacity);}"; // eslint-disable-line
|
|
41367
40935
|
|
|
41368
40936
|
let planeWidth = 80;
|
|
41369
40937
|
let planeHeight = 80;
|
|
41370
|
-
|
|
41371
|
-
|
|
41372
|
-
|
|
41373
|
-
|
|
41374
|
-
|
|
41375
|
-
|
|
41376
|
-
|
|
41377
|
-
|
|
41378
|
-
|
|
41379
|
-
|
|
41380
|
-
|
|
41381
|
-
diffuse: { value: texture },
|
|
41382
|
-
depth: { value: 1 },
|
|
41383
|
-
size: { value: new Vector2(planeWidth, planeHeight) },
|
|
41384
|
-
},
|
|
41385
|
-
vertexShader: vert_2d,
|
|
41386
|
-
fragmentShader: frag_2d,
|
|
41387
|
-
glslVersion: GLSL3,
|
|
41388
|
-
side: DoubleSide,
|
|
41389
|
-
});
|
|
41390
|
-
const geometry = new PlaneGeometry(planeWidth, planeHeight);
|
|
41391
|
-
const mesh = new Mesh(geometry, material);
|
|
41392
|
-
mesh.name = "texture2d_mesh_zip";
|
|
41393
|
-
scene.add(mesh);
|
|
41394
|
-
});
|
|
40938
|
+
// Build a quad from the 4 world-space image-plane corners (preserves real pose).
|
|
40939
|
+
function buildAlignedQuad(c) {
|
|
40940
|
+
const g = new BufferGeometry();
|
|
40941
|
+
// two triangles: tl,bl,tr / tr,bl,br
|
|
40942
|
+
g.setAttribute("position", new BufferAttribute(new Float32Array([...c.tl, ...c.bl, ...c.tr, ...c.tr, ...c.bl, ...c.br]), 3));
|
|
40943
|
+
// vertex order is tl, bl, tr, tr, bl, br. The fragment shader flips v (vUv.y = 1-uv.y)
|
|
40944
|
+
// and DataArrayTexture has no flipY, so the image-top (data row 0) must map to the
|
|
40945
|
+
// top world corner: tl/tr get uv.y = 1.
|
|
40946
|
+
g.setAttribute("uv", new BufferAttribute(new Float32Array([0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0]), 2));
|
|
40947
|
+
g.computeVertexNormals();
|
|
40948
|
+
return g;
|
|
41395
40949
|
}
|
|
41396
40950
|
function createTexture2D_NRRD(data, width, height, depth, callback) {
|
|
41397
40951
|
const texture = new DataArrayTexture(data, width, height, depth);
|
|
@@ -41401,7 +40955,7 @@ function createTexture2D_NRRD(data, width, height, depth, callback) {
|
|
|
41401
40955
|
uniforms: {
|
|
41402
40956
|
diffuse: { value: texture },
|
|
41403
40957
|
depth: { value: 1 },
|
|
41404
|
-
|
|
40958
|
+
uOpacity: { value: 1 },
|
|
41405
40959
|
},
|
|
41406
40960
|
vertexShader: vert_2d,
|
|
41407
40961
|
fragmentShader: frag_2d,
|
|
@@ -41413,13 +40967,9 @@ function createTexture2D_NRRD(data, width, height, depth, callback) {
|
|
|
41413
40967
|
mesh.name = "texture2d_mesh_zip";
|
|
41414
40968
|
callback(mesh);
|
|
41415
40969
|
}
|
|
41416
|
-
function createTexture2D_Array(copperVolume, depth, scene, gui) {
|
|
40970
|
+
function createTexture2D_Array(copperVolume, depth, scene, gui, aligned = false) {
|
|
41417
40971
|
planeWidth = copperVolume.width / 2;
|
|
41418
40972
|
planeHeight = copperVolume.height / 2;
|
|
41419
|
-
({
|
|
41420
|
-
windowWidth: copperVolume.windowWidth,
|
|
41421
|
-
windowCenter: copperVolume.windowCenter,
|
|
41422
|
-
});
|
|
41423
40973
|
const texture = new DataArrayTexture(copperVolume.uint8, copperVolume.width, copperVolume.height, depth);
|
|
41424
40974
|
texture.format = RedFormat;
|
|
41425
40975
|
texture.needsUpdate = true;
|
|
@@ -41446,18 +40996,22 @@ function createTexture2D_Array(copperVolume, depth, scene, gui) {
|
|
|
41446
40996
|
const material = new ShaderMaterial({
|
|
41447
40997
|
uniforms: {
|
|
41448
40998
|
diffuse: { value: texture },
|
|
41449
|
-
depth: { value:
|
|
41450
|
-
|
|
40999
|
+
depth: { value: 0 },
|
|
41000
|
+
uOpacity: { value: 1 },
|
|
41451
41001
|
},
|
|
41452
41002
|
vertexShader: vert_2d,
|
|
41453
41003
|
fragmentShader: frag_2d,
|
|
41454
41004
|
glslVersion: GLSL3,
|
|
41455
41005
|
side: DoubleSide,
|
|
41456
41006
|
});
|
|
41457
|
-
|
|
41007
|
+
// Aligned world-space quad only when explicitly requested and geometry tags exist;
|
|
41008
|
+
// otherwise the legacy centred plane (keeps existing loadDicom behaviour unchanged).
|
|
41009
|
+
const geometry = aligned && copperVolume.corners
|
|
41010
|
+
? buildAlignedQuad(copperVolume.corners)
|
|
41011
|
+
: new PlaneGeometry(planeWidth, planeHeight);
|
|
41458
41012
|
const mesh = new Mesh(geometry, material);
|
|
41459
|
-
scene.add(mesh);
|
|
41460
41013
|
mesh.name = "texture2d_mesh_array";
|
|
41014
|
+
scene.add(mesh);
|
|
41461
41015
|
function updateTexture(copperVolumeUp) {
|
|
41462
41016
|
if (!!copperVolumeUp) {
|
|
41463
41017
|
let voiLUT;
|
|
@@ -41468,7 +41022,15 @@ function createTexture2D_Array(copperVolume, depth, scene, gui) {
|
|
|
41468
41022
|
texture.needsUpdate = true;
|
|
41469
41023
|
}
|
|
41470
41024
|
}
|
|
41471
|
-
|
|
41025
|
+
function setFrame(i) {
|
|
41026
|
+
material.uniforms.depth.value = i;
|
|
41027
|
+
}
|
|
41028
|
+
function setWindow(center, width) {
|
|
41029
|
+
copperVolume.windowCenter = center;
|
|
41030
|
+
copperVolume.windowWidth = width;
|
|
41031
|
+
updateTexture(copperVolume);
|
|
41032
|
+
}
|
|
41033
|
+
return { mesh, copperVolume, updateTexture, setFrame, setWindow };
|
|
41472
41034
|
}
|
|
41473
41035
|
|
|
41474
41036
|
/*!
|
|
@@ -59300,6 +58862,7 @@ function onTouchEnd( event ) {
|
|
|
59300
58862
|
}
|
|
59301
58863
|
|
|
59302
58864
|
class copperScene extends baseScene {
|
|
58865
|
+
// rayster pick
|
|
59303
58866
|
constructor(container, renderer, opt) {
|
|
59304
58867
|
super(container, renderer, opt);
|
|
59305
58868
|
this.clock = new Clock();
|
|
@@ -59307,9 +58870,6 @@ class copperScene extends baseScene {
|
|
|
59307
58870
|
this.mixer = null;
|
|
59308
58871
|
this.playRate = 1.0;
|
|
59309
58872
|
this.modelReady = false;
|
|
59310
|
-
// rayster pick
|
|
59311
|
-
// texture2d
|
|
59312
|
-
this.texture2dMesh = null;
|
|
59313
58873
|
if ((opt === null || opt === void 0 ? void 0 : opt.controls) === "trackball") {
|
|
59314
58874
|
this.controls = new TrackballControls(this.camera, this.renderer.domElement);
|
|
59315
58875
|
}
|
|
@@ -59515,38 +59075,177 @@ class copperScene extends baseScene {
|
|
|
59515
59075
|
this.exportContent.animations.push(clip);
|
|
59516
59076
|
};
|
|
59517
59077
|
}
|
|
59518
|
-
|
|
59519
|
-
|
|
59520
|
-
|
|
59521
|
-
|
|
59522
|
-
|
|
59523
|
-
|
|
59524
|
-
|
|
59525
|
-
|
|
59526
|
-
|
|
59527
|
-
|
|
59528
|
-
|
|
59529
|
-
|
|
59530
|
-
|
|
59531
|
-
|
|
59532
|
-
|
|
59533
|
-
|
|
59534
|
-
|
|
59535
|
-
|
|
59536
|
-
|
|
59537
|
-
|
|
59538
|
-
|
|
59539
|
-
|
|
59540
|
-
|
|
59541
|
-
|
|
59542
|
-
|
|
59543
|
-
}
|
|
59544
|
-
|
|
59078
|
+
/**
|
|
59079
|
+
* Load an aligned 4D scene: a cine MRI (one slice, N cardiac phases) rendered as a
|
|
59080
|
+
* world-placed plane, plus 0..N deforming surface sequences (e.g. LV endo/epi). MRI
|
|
59081
|
+
* and surfaces stay in their shared patient coordinate frame (no center/scale), and
|
|
59082
|
+
* advance together off ONE shared frame clock. Returns a controller (play/pause/...).
|
|
59083
|
+
*/
|
|
59084
|
+
loadAligned4D(opts, callback) {
|
|
59085
|
+
var _a;
|
|
59086
|
+
const dicomDepth = opts.dicomUrls.length;
|
|
59087
|
+
const loadDicomStack = () => new Promise((resolve) => {
|
|
59088
|
+
const volumes = [];
|
|
59089
|
+
opts.dicomUrls.forEach((url) => {
|
|
59090
|
+
copperDicomLoader(url, (volume) => {
|
|
59091
|
+
volumes.push(volume);
|
|
59092
|
+
if (volumes.length !== dicomDepth)
|
|
59093
|
+
return;
|
|
59094
|
+
// order frames by cardiac phase (TriggerTime / SliceLocation)
|
|
59095
|
+
volumes.sort((a, b) => a.order - b.order);
|
|
59096
|
+
const frameSize = volumes[0].width * volumes[0].height;
|
|
59097
|
+
const uint8 = new Uint8ClampedArray(frameSize * dicomDepth);
|
|
59098
|
+
const uint16 = new Uint16Array(uint8.length);
|
|
59099
|
+
volumes.forEach((v, index) => {
|
|
59100
|
+
uint8.set(v.uint8, index * frameSize);
|
|
59101
|
+
uint16.set(v.uint16, index * frameSize);
|
|
59102
|
+
});
|
|
59103
|
+
const stacked = Object.assign(Object.assign({}, volumes[0]), { uint8,
|
|
59104
|
+
uint16 });
|
|
59105
|
+
resolve(stacked);
|
|
59106
|
+
});
|
|
59545
59107
|
});
|
|
59546
|
-
|
|
59547
|
-
|
|
59108
|
+
});
|
|
59109
|
+
const loadSurfaceSequence = (urls) => new Promise((resolve) => {
|
|
59110
|
+
const loader = new VTKLoader();
|
|
59111
|
+
const geometries = new Array(urls.length);
|
|
59112
|
+
let done = 0;
|
|
59113
|
+
urls.forEach((url, index) => {
|
|
59114
|
+
loader.load(url, (geometry) => {
|
|
59115
|
+
// keep world coordinates — no center()/scale()
|
|
59116
|
+
geometry.computeVertexNormals();
|
|
59117
|
+
geometries[index] = geometry;
|
|
59118
|
+
if (++done === urls.length)
|
|
59119
|
+
resolve(geometries);
|
|
59120
|
+
});
|
|
59121
|
+
});
|
|
59122
|
+
});
|
|
59123
|
+
const surfaceDefs = (_a = opts.surfaces) !== null && _a !== void 0 ? _a : [];
|
|
59124
|
+
Promise.all([
|
|
59125
|
+
loadDicomStack(),
|
|
59126
|
+
...surfaceDefs.map((s) => loadSurfaceSequence(s.urls)),
|
|
59127
|
+
]).then(([stacked, ...surfaceGeoms]) => {
|
|
59128
|
+
var _a;
|
|
59129
|
+
if (opts.window) {
|
|
59130
|
+
stacked.windowCenter = opts.window.center;
|
|
59131
|
+
stacked.windowWidth = opts.window.width;
|
|
59548
59132
|
}
|
|
59549
|
-
|
|
59133
|
+
const tex = createTexture2D_Array(stacked, dicomDepth, this.scene, undefined, true);
|
|
59134
|
+
tex.setFrame(0);
|
|
59135
|
+
if (opts.window)
|
|
59136
|
+
tex.setWindow(opts.window.center, opts.window.width);
|
|
59137
|
+
const surfaceMeshes = {};
|
|
59138
|
+
const seqs = [];
|
|
59139
|
+
surfaceDefs.forEach((def, i) => {
|
|
59140
|
+
var _a;
|
|
59141
|
+
const geometries = surfaceGeoms[i];
|
|
59142
|
+
const { vtkmaterial } = copperMultipleVtk(def.opts);
|
|
59143
|
+
const mesh = new Mesh(geometries[0], vtkmaterial);
|
|
59144
|
+
mesh.name = def.name;
|
|
59145
|
+
this.scene.add(mesh);
|
|
59146
|
+
surfaceMeshes[def.name] = mesh;
|
|
59147
|
+
seqs.push({ mesh, geometries, offset: (_a = def.offset) !== null && _a !== void 0 ? _a : 0 });
|
|
59148
|
+
});
|
|
59149
|
+
const frameCount = dicomDepth;
|
|
59150
|
+
// Default one cardiac cycle ≈ 1012ms (32 phases × ~31.6ms) unless overridden.
|
|
59151
|
+
const cycleMs = (_a = opts.cycleMs) !== null && _a !== void 0 ? _a : 1012;
|
|
59152
|
+
let frameIndex = 0;
|
|
59153
|
+
let speed = 1;
|
|
59154
|
+
let playing = true;
|
|
59155
|
+
let disposed = false;
|
|
59156
|
+
let lastStep = performance.now();
|
|
59157
|
+
const dtBase = cycleMs / frameCount;
|
|
59158
|
+
const applyFrame = () => {
|
|
59159
|
+
tex.setFrame(frameIndex);
|
|
59160
|
+
for (const s of seqs) {
|
|
59161
|
+
const i = (frameIndex + s.offset + s.geometries.length) % s.geometries.length;
|
|
59162
|
+
s.mesh.geometry = s.geometries[i];
|
|
59163
|
+
}
|
|
59164
|
+
};
|
|
59165
|
+
const tick = () => {
|
|
59166
|
+
if (disposed || !playing)
|
|
59167
|
+
return;
|
|
59168
|
+
const now = performance.now();
|
|
59169
|
+
if (now - lastStep >= dtBase / speed) {
|
|
59170
|
+
lastStep = now;
|
|
59171
|
+
frameIndex = (frameIndex + 1) % frameCount;
|
|
59172
|
+
applyFrame();
|
|
59173
|
+
}
|
|
59174
|
+
};
|
|
59175
|
+
this.addPreRenderCallbackFunction(tick);
|
|
59176
|
+
const clockId = tick.id;
|
|
59177
|
+
const ctrl = {
|
|
59178
|
+
plane: tex.mesh,
|
|
59179
|
+
surfaceMeshes,
|
|
59180
|
+
frameCount,
|
|
59181
|
+
play: () => {
|
|
59182
|
+
playing = true;
|
|
59183
|
+
lastStep = performance.now();
|
|
59184
|
+
},
|
|
59185
|
+
pause: () => {
|
|
59186
|
+
playing = false;
|
|
59187
|
+
},
|
|
59188
|
+
toggle: () => {
|
|
59189
|
+
playing = !playing;
|
|
59190
|
+
lastStep = performance.now();
|
|
59191
|
+
},
|
|
59192
|
+
setSpeed: (x) => {
|
|
59193
|
+
speed = Math.max(0.01, x);
|
|
59194
|
+
},
|
|
59195
|
+
setFrame: (i) => {
|
|
59196
|
+
frameIndex = ((i % frameCount) + frameCount) % frameCount;
|
|
59197
|
+
applyFrame();
|
|
59198
|
+
},
|
|
59199
|
+
setFrameOffset: (name, n) => {
|
|
59200
|
+
const s = seqs.find((q) => q.mesh.name === name);
|
|
59201
|
+
if (s) {
|
|
59202
|
+
s.offset = n;
|
|
59203
|
+
applyFrame();
|
|
59204
|
+
}
|
|
59205
|
+
},
|
|
59206
|
+
setWindow: (center, width) => {
|
|
59207
|
+
tex.setWindow(center, width);
|
|
59208
|
+
},
|
|
59209
|
+
setPlaneOpacity: (v) => {
|
|
59210
|
+
const m = tex.mesh.material;
|
|
59211
|
+
m.transparent = v < 1;
|
|
59212
|
+
m.uniforms.uOpacity.value = v;
|
|
59213
|
+
m.needsUpdate = true;
|
|
59214
|
+
},
|
|
59215
|
+
setSurfaceOpacity: (name, v) => {
|
|
59216
|
+
const mesh = surfaceMeshes[name];
|
|
59217
|
+
if (!mesh)
|
|
59218
|
+
return;
|
|
59219
|
+
const m = mesh.material;
|
|
59220
|
+
m.transparent = v < 1;
|
|
59221
|
+
m.opacity = v;
|
|
59222
|
+
m.needsUpdate = true;
|
|
59223
|
+
},
|
|
59224
|
+
setSurfaceVisible: (name, visible) => {
|
|
59225
|
+
const mesh = surfaceMeshes[name];
|
|
59226
|
+
if (mesh)
|
|
59227
|
+
mesh.visible = visible;
|
|
59228
|
+
},
|
|
59229
|
+
dispose: () => {
|
|
59230
|
+
var _a, _b;
|
|
59231
|
+
disposed = true;
|
|
59232
|
+
this.removePreRenderCallbackFunction(clockId);
|
|
59233
|
+
// plane
|
|
59234
|
+
this.scene.remove(tex.mesh);
|
|
59235
|
+
tex.mesh.geometry.dispose();
|
|
59236
|
+
const pm = tex.mesh.material;
|
|
59237
|
+
(_b = (_a = pm.uniforms.diffuse.value) === null || _a === void 0 ? void 0 : _a.dispose) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
59238
|
+
pm.dispose();
|
|
59239
|
+
// surfaces
|
|
59240
|
+
for (const s of seqs) {
|
|
59241
|
+
this.scene.remove(s.mesh);
|
|
59242
|
+
s.geometries.forEach((g) => g.dispose());
|
|
59243
|
+
s.mesh.material.dispose();
|
|
59244
|
+
}
|
|
59245
|
+
},
|
|
59246
|
+
};
|
|
59247
|
+
callback && callback(ctrl);
|
|
59248
|
+
});
|
|
59550
59249
|
}
|
|
59551
59250
|
getPlayRate() {
|
|
59552
59251
|
return this.playRate;
|
|
@@ -59652,8 +59351,11 @@ class copperRenderer extends baseRenderer {
|
|
|
59652
59351
|
this.delta = 0;
|
|
59653
59352
|
this.interval = 1 / this.fps;
|
|
59654
59353
|
this.preRenderCallbackFunctions = [];
|
|
59354
|
+
this.running = true;
|
|
59655
59355
|
this.animate = (time) => {
|
|
59656
59356
|
var _a, _b, _c;
|
|
59357
|
+
if (!this.running)
|
|
59358
|
+
return;
|
|
59657
59359
|
switch ((_a = this.options) === null || _a === void 0 ? void 0 : _a.fpsMode) {
|
|
59658
59360
|
case "1":
|
|
59659
59361
|
// fpsControl one: 30fps
|
|
@@ -59680,6 +59382,10 @@ class copperRenderer extends baseRenderer {
|
|
|
59680
59382
|
}
|
|
59681
59383
|
};
|
|
59682
59384
|
}
|
|
59385
|
+
// Stop the render loop (call on teardown to release the RAF and let GC reclaim).
|
|
59386
|
+
stop() {
|
|
59387
|
+
this.running = false;
|
|
59388
|
+
}
|
|
59683
59389
|
getSceneByName(name) {
|
|
59684
59390
|
return this.sceneMap[name];
|
|
59685
59391
|
}
|
|
@@ -78410,6 +78116,12 @@ class NrrdTools {
|
|
|
78410
78116
|
updateWindowLow: this.guiParameterSettings.windowLow.onChange,
|
|
78411
78117
|
finishContrastAdjustment: this.guiParameterSettings.windowHigh.onFinished,
|
|
78412
78118
|
};
|
|
78119
|
+
// setupGUI may run AFTER images are loaded (e.g. the register/origin-switch load
|
|
78120
|
+
// path), in which case the on-load syncGuiParameterSettings() ran while
|
|
78121
|
+
// guiParameterSettings was still undefined and silently skipped the binding. Bind
|
|
78122
|
+
// now so the window slider reflects the current volume's real min/max/window instead
|
|
78123
|
+
// of the default 0/0 (which froze the slider and turned the image white on zoom).
|
|
78124
|
+
this.syncGuiParameterSettings();
|
|
78413
78125
|
}
|
|
78414
78126
|
/**
|
|
78415
78127
|
* Sync guiParameterSettings with current volume metadata.
|
|
@@ -79442,7 +79154,7 @@ function evaluateElement(element, weights) {
|
|
|
79442
79154
|
}
|
|
79443
79155
|
|
|
79444
79156
|
// import * as kiwrious from "copper3d_plugin_heart_k";
|
|
79445
|
-
const REVISION = "v3.4.
|
|
79157
|
+
const REVISION = "v3.4.9-beta";
|
|
79446
79158
|
console.log(`%cCopper3D Visualisation %cBeta:${REVISION}`, "padding: 3px;color:white; background:#023047", "padding: 3px;color:white; background:#f50a25");
|
|
79447
79159
|
|
|
79448
79160
|
export { CHANNEL_COLORS, CHANNEL_HEX_COLORS, CameraViewPoint, Copper3dTrackballControls, GaussianSmoother, MeshNodeTool, NrrdTools, REVISION, addBoxHelper, addLabelToScene, configKiwriousHeart, convert3DPostoScreenPos, convertScreenPosto3DPos, copperMScene, copperMSceneRenderer, copperRenderer, copperRendererOnDemond, copperScene, copperSceneOnDemond, createTexture2D_NRRD, fullScreenListenner, kiwrious, loading, removeGuiFolderChilden, rgbaToCss, rgbaToHex, setHDRFilePath, throttle };
|