@vitessce/all 3.9.6 → 4.0.0-test.0
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/GeometryAndMeshXR-Bewsp5O-.js +247 -0
- package/dist/{ReactNeuroglancer-Cl5UUROE.js → ReactNeuroglancer-CGctZU7S.js} +3 -2
- package/dist/XREnterButton-vmEhdF4L.js +52 -0
- package/dist/XRSceneComponents-DnZLSAJz.js +62 -0
- package/dist/XRWrapper-C94X82Mb.js +9 -0
- package/dist/{deflate-BXY2A2Ns.js → deflate-DACPPK4G.js} +1 -1
- package/dist/{higlass-CQEpQgZz.js → higlass-DPu0XbrS.js} +42246 -38959
- package/dist/{index-B1IhEdcb.js → index-5W6nu-ix.js} +6180 -40053
- package/dist/{index-B8V4ICbL.js → index-DXuQcXKb.js} +20 -16
- package/dist/index-DxpZAn2M.js +1270 -0
- package/dist/index.js +8 -8
- package/dist/{jpeg-D6QPlXZC.js → jpeg-fpomMAyh.js} +1 -1
- package/dist/{lerc-Cbf6R7GS.js → lerc-5t7sN-sd.js} +1 -1
- package/dist/{lzw-DSrSjczU.js → lzw-CC_Aw25a.js} +1 -1
- package/dist/{packbits-9-KGrvk3.js → packbits-VBSAsHuN.js} +1 -1
- package/dist/{raw-B9-e46aL.js → raw-DK74vK-V.js} +1 -1
- package/dist/{webimage-Cy-EhPIx.js → webimage-BdYU0dUW.js} +1 -1
- package/dist/xrStore-BymedRtx.js +8 -0
- package/package.json +54 -36
- package/dist/OrbitControls-BGcAu271.js +0 -18694
- package/dist/index-DKPVlERU.js +0 -9854
- package/dist/troika-three-text.esm-BKSLyjBY.js +0 -5685
|
@@ -0,0 +1,1270 @@
|
|
|
1
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import React__default, { useRef, useState, useEffect, Suspense, forwardRef } from "react";
|
|
3
|
+
import { Canvas } from "@react-three/fiber";
|
|
4
|
+
import { Center, Text, Line, Bvh, OrbitControls } from "@react-three/drei";
|
|
5
|
+
import { Matrix3, Matrix4, Vector3, Vector2, UniformsUtils, Data3DTexture, RedFormat, FloatType, LinearFilter, FrontSide, Scene, Group, MeshPhysicalMaterial, MeshBasicMaterial, MeshStandardMaterial, BackSide } from "three";
|
|
6
|
+
import { C as CoordinationType, g as getImageSize } from "./index-5W6nu-ix.js";
|
|
7
|
+
function MeasureLine({ currentLine, scale }) {
|
|
8
|
+
const textRef = useRef(null);
|
|
9
|
+
return jsxs("group", { children: [jsx(Center, { bottom: true, right: true, position: [currentLine.midPoint.x, currentLine.midPoint.y, currentLine.midPoint.z], rotation: [0, 0, 0], children: jsx(Text, { color: "gray", scale: 0.05, ref: textRef, children: `${(currentLine.startPoint.distanceTo(currentLine.endPoint) * scale).toFixed(2)} µm` }) }), jsx(Line, {
|
|
10
|
+
points: [currentLine.startPoint, currentLine.endPoint],
|
|
11
|
+
color: "white",
|
|
12
|
+
lineWidth: 2,
|
|
13
|
+
dashed: false,
|
|
14
|
+
segments: true
|
|
15
|
+
})] });
|
|
16
|
+
}
|
|
17
|
+
class Volume {
|
|
18
|
+
spacing = [1, 1, 1];
|
|
19
|
+
offset = [0, 0, 0];
|
|
20
|
+
matrix = new Matrix3().identity();
|
|
21
|
+
sliceList = [];
|
|
22
|
+
lowerThresholdValue = -Infinity;
|
|
23
|
+
upperThresholdValue = Infinity;
|
|
24
|
+
xLength = 1;
|
|
25
|
+
yLength = 1;
|
|
26
|
+
zLength = 1;
|
|
27
|
+
data;
|
|
28
|
+
RASDimensions;
|
|
29
|
+
inverseMatrix;
|
|
30
|
+
min;
|
|
31
|
+
max;
|
|
32
|
+
constructor(xLength, yLength, zLength, type, arrayBuffer) {
|
|
33
|
+
if (xLength !== void 0) {
|
|
34
|
+
this.xLength = Number(xLength) || 1;
|
|
35
|
+
this.yLength = Number(yLength) || 1;
|
|
36
|
+
this.zLength = Number(zLength) || 1;
|
|
37
|
+
switch (type) {
|
|
38
|
+
case "Uint8":
|
|
39
|
+
case "uint8":
|
|
40
|
+
case "uchar":
|
|
41
|
+
case "unsigned char":
|
|
42
|
+
case "uint8_t":
|
|
43
|
+
this.data = new Uint8Array(arrayBuffer);
|
|
44
|
+
break;
|
|
45
|
+
case "Int8":
|
|
46
|
+
case "int8":
|
|
47
|
+
case "signed char":
|
|
48
|
+
case "int8_t":
|
|
49
|
+
this.data = new Int8Array(arrayBuffer);
|
|
50
|
+
break;
|
|
51
|
+
case "Int16":
|
|
52
|
+
case "int16":
|
|
53
|
+
case "short":
|
|
54
|
+
case "short int":
|
|
55
|
+
case "signed short":
|
|
56
|
+
case "signed short int":
|
|
57
|
+
case "int16_t":
|
|
58
|
+
this.data = new Int16Array(arrayBuffer);
|
|
59
|
+
break;
|
|
60
|
+
case "Uint16":
|
|
61
|
+
case "uint16":
|
|
62
|
+
case "ushort":
|
|
63
|
+
case "unsigned short":
|
|
64
|
+
case "unsigned short int":
|
|
65
|
+
case "uint16_t":
|
|
66
|
+
this.data = new Uint16Array(arrayBuffer);
|
|
67
|
+
break;
|
|
68
|
+
case "Int32":
|
|
69
|
+
case "int32":
|
|
70
|
+
case "int":
|
|
71
|
+
case "signed int":
|
|
72
|
+
case "int32_t":
|
|
73
|
+
this.data = new Int32Array(arrayBuffer);
|
|
74
|
+
break;
|
|
75
|
+
case "Uint32":
|
|
76
|
+
case "uint32":
|
|
77
|
+
case "uint":
|
|
78
|
+
case "unsigned int":
|
|
79
|
+
case "uint32_t":
|
|
80
|
+
this.data = new Uint32Array(arrayBuffer);
|
|
81
|
+
break;
|
|
82
|
+
case "longlong":
|
|
83
|
+
case "long long":
|
|
84
|
+
case "long long int":
|
|
85
|
+
case "signed long long":
|
|
86
|
+
case "signed long long int":
|
|
87
|
+
case "int64":
|
|
88
|
+
case "int64_t":
|
|
89
|
+
case "ulonglong":
|
|
90
|
+
case "unsigned long long":
|
|
91
|
+
case "unsigned long long int":
|
|
92
|
+
case "uint64":
|
|
93
|
+
case "uint64_t":
|
|
94
|
+
throw new Error("uint64_t type is not supported in JavaScript");
|
|
95
|
+
case "Float32":
|
|
96
|
+
case "float32":
|
|
97
|
+
case "float":
|
|
98
|
+
this.data = new Float32Array(arrayBuffer);
|
|
99
|
+
break;
|
|
100
|
+
case "Float64":
|
|
101
|
+
case "float64":
|
|
102
|
+
case "double":
|
|
103
|
+
this.data = new Float64Array(arrayBuffer);
|
|
104
|
+
break;
|
|
105
|
+
default:
|
|
106
|
+
this.data = new Uint8Array(arrayBuffer);
|
|
107
|
+
}
|
|
108
|
+
if (this.data.length !== this.xLength * this.yLength * this.zLength) {
|
|
109
|
+
throw new Error("lengths are not matching arrayBuffer size");
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
get lowerThreshold() {
|
|
114
|
+
return this.lowerThresholdValue;
|
|
115
|
+
}
|
|
116
|
+
set lowerThreshold(value) {
|
|
117
|
+
this.lowerThresholdValue = value;
|
|
118
|
+
this.sliceList.forEach((slice) => {
|
|
119
|
+
slice.geometryNeedsUpdate = true;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
get upperThreshold() {
|
|
123
|
+
return this.upperThresholdValue;
|
|
124
|
+
}
|
|
125
|
+
set upperThreshold(value) {
|
|
126
|
+
this.upperThresholdValue = value;
|
|
127
|
+
this.sliceList.forEach((slice) => {
|
|
128
|
+
slice.geometryNeedsUpdate = true;
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
getData(i, j, k) {
|
|
132
|
+
return this.data[k * this.xLength * this.yLength + j * this.xLength + i];
|
|
133
|
+
}
|
|
134
|
+
access(i, j, k) {
|
|
135
|
+
return k * this.xLength * this.yLength + j * this.xLength + i;
|
|
136
|
+
}
|
|
137
|
+
reverseAccess(index2) {
|
|
138
|
+
const z = Math.floor(index2 / (this.yLength * this.xLength));
|
|
139
|
+
const y = Math.floor((index2 - z * this.yLength * this.xLength) / this.xLength);
|
|
140
|
+
const x = index2 - z * this.yLength * this.xLength - y * this.xLength;
|
|
141
|
+
return [x, y, z];
|
|
142
|
+
}
|
|
143
|
+
// eslint-disable-next-line max-len
|
|
144
|
+
map(functionToMap, contextParam) {
|
|
145
|
+
const { length } = this.data;
|
|
146
|
+
const context = contextParam || this;
|
|
147
|
+
for (let i = 0; i < length; i++) {
|
|
148
|
+
this.data[i] = functionToMap.call(context, this.data[i], i, this.data);
|
|
149
|
+
}
|
|
150
|
+
return this;
|
|
151
|
+
}
|
|
152
|
+
extractPerpendicularPlane(axis, RASIndex) {
|
|
153
|
+
const planeMatrix = new Matrix4().identity();
|
|
154
|
+
const volume = this;
|
|
155
|
+
let firstSpacing;
|
|
156
|
+
let secondSpacing;
|
|
157
|
+
let positionOffset;
|
|
158
|
+
let IJKIndex;
|
|
159
|
+
const axisInIJK = new Vector3();
|
|
160
|
+
const firstDirection = new Vector3();
|
|
161
|
+
const secondDirection = new Vector3();
|
|
162
|
+
const dimensions = new Vector3(this.xLength, this.yLength, this.zLength);
|
|
163
|
+
switch (axis) {
|
|
164
|
+
case "x":
|
|
165
|
+
axisInIJK.set(1, 0, 0);
|
|
166
|
+
firstDirection.set(0, 0, -1);
|
|
167
|
+
secondDirection.set(0, -1, 0);
|
|
168
|
+
firstSpacing = this.spacing[2];
|
|
169
|
+
secondSpacing = this.spacing[1];
|
|
170
|
+
IJKIndex = new Vector3(RASIndex, 0, 0);
|
|
171
|
+
planeMatrix.multiply(new Matrix4().makeRotationY(Math.PI / 2));
|
|
172
|
+
positionOffset = (volume.RASDimensions[0] - 1) / 2;
|
|
173
|
+
planeMatrix.setPosition(new Vector3(RASIndex - positionOffset, 0, 0));
|
|
174
|
+
break;
|
|
175
|
+
case "y":
|
|
176
|
+
axisInIJK.set(0, 1, 0);
|
|
177
|
+
firstDirection.set(1, 0, 0);
|
|
178
|
+
secondDirection.set(0, 0, 1);
|
|
179
|
+
firstSpacing = this.spacing[0];
|
|
180
|
+
secondSpacing = this.spacing[2];
|
|
181
|
+
IJKIndex = new Vector3(0, RASIndex, 0);
|
|
182
|
+
planeMatrix.multiply(new Matrix4().makeRotationX(-Math.PI / 2));
|
|
183
|
+
positionOffset = (volume.RASDimensions[1] - 1) / 2;
|
|
184
|
+
planeMatrix.setPosition(new Vector3(0, RASIndex - positionOffset, 0));
|
|
185
|
+
break;
|
|
186
|
+
case "z":
|
|
187
|
+
default:
|
|
188
|
+
axisInIJK.set(0, 0, 1);
|
|
189
|
+
firstDirection.set(1, 0, 0);
|
|
190
|
+
secondDirection.set(0, -1, 0);
|
|
191
|
+
firstSpacing = this.spacing[0];
|
|
192
|
+
secondSpacing = this.spacing[1];
|
|
193
|
+
IJKIndex = new Vector3(0, 0, RASIndex);
|
|
194
|
+
positionOffset = (volume.RASDimensions[2] - 1) / 2;
|
|
195
|
+
planeMatrix.setPosition(new Vector3(0, 0, RASIndex - positionOffset));
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
firstDirection.applyMatrix4(volume.inverseMatrix).normalize();
|
|
199
|
+
firstDirection.argVar = "i";
|
|
200
|
+
secondDirection.applyMatrix4(volume.inverseMatrix).normalize();
|
|
201
|
+
secondDirection.argVar = "j";
|
|
202
|
+
axisInIJK.applyMatrix4(volume.inverseMatrix).normalize();
|
|
203
|
+
const iLength = Math.floor(Math.abs(firstDirection.dot(dimensions)));
|
|
204
|
+
const jLength = Math.floor(Math.abs(secondDirection.dot(dimensions)));
|
|
205
|
+
const planeWidth = Math.abs(iLength * firstSpacing);
|
|
206
|
+
const planeHeight = Math.abs(jLength * secondSpacing);
|
|
207
|
+
IJKIndex = Math.abs(Math.round(IJKIndex.applyMatrix4(volume.inverseMatrix).dot(axisInIJK)));
|
|
208
|
+
const base = [new Vector3(1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1)];
|
|
209
|
+
const iDirection = [firstDirection, secondDirection, axisInIJK].find((x) => Math.abs(x.dot(base[0])) > 0.9);
|
|
210
|
+
const jDirection = [firstDirection, secondDirection, axisInIJK].find((x) => Math.abs(x.dot(base[1])) > 0.9);
|
|
211
|
+
const kDirection = [firstDirection, secondDirection, axisInIJK].find((x) => Math.abs(x.dot(base[2])) > 0.9);
|
|
212
|
+
function sliceAccess(i, j) {
|
|
213
|
+
const si = iDirection === axisInIJK ? IJKIndex : iDirection.argVar === "i" ? i : j;
|
|
214
|
+
const sj = jDirection === axisInIJK ? IJKIndex : jDirection.argVar === "i" ? i : j;
|
|
215
|
+
const sk = kDirection === axisInIJK ? IJKIndex : kDirection.argVar === "i" ? i : j;
|
|
216
|
+
const accessI = iDirection.dot(base[0]) > 0 ? si : volume.xLength - 1 - si;
|
|
217
|
+
const accessJ = jDirection.dot(base[1]) > 0 ? sj : volume.yLength - 1 - sj;
|
|
218
|
+
const accessK = kDirection.dot(base[2]) > 0 ? sk : volume.zLength - 1 - sk;
|
|
219
|
+
return volume.access(accessI, accessJ, accessK);
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
iLength,
|
|
223
|
+
jLength,
|
|
224
|
+
sliceAccess,
|
|
225
|
+
matrix: planeMatrix,
|
|
226
|
+
planeWidth,
|
|
227
|
+
planeHeight
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
computeMinMax() {
|
|
231
|
+
let min = Infinity;
|
|
232
|
+
let max = -Infinity;
|
|
233
|
+
const datasize = this.data.length;
|
|
234
|
+
let i = 0;
|
|
235
|
+
for (i = 0; i < datasize; i++) {
|
|
236
|
+
if (!Number.isNaN(this.data[i])) {
|
|
237
|
+
const value = this.data[i];
|
|
238
|
+
min = Math.min(min, value);
|
|
239
|
+
max = Math.max(max, value);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
this.min = min;
|
|
243
|
+
this.max = max;
|
|
244
|
+
return [min, max];
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const VolumeRenderShaderPerspective = {
|
|
248
|
+
uniforms: {
|
|
249
|
+
u_size: { value: new Vector3(1, 1, 1) },
|
|
250
|
+
u_renderstyle: { value: 0 },
|
|
251
|
+
u_renderthreshold: { value: 0.5 },
|
|
252
|
+
u_opacity: { value: 0.5 },
|
|
253
|
+
u_clim: { value: new Vector2(0.2, 0.8) },
|
|
254
|
+
u_clim2: { value: new Vector2(0.2, 0.8) },
|
|
255
|
+
u_clim3: { value: new Vector2(0.2, 0.8) },
|
|
256
|
+
u_clim4: { value: new Vector2(0.2, 0.8) },
|
|
257
|
+
u_clim5: { value: new Vector2(0.2, 0.8) },
|
|
258
|
+
u_clim6: { value: new Vector2(0.2, 0.8) },
|
|
259
|
+
u_xClip: { value: new Vector2(-1, 1e6) },
|
|
260
|
+
u_yClip: { value: new Vector2(-1, 1e6) },
|
|
261
|
+
u_zClip: { value: new Vector2(-1, 1e6) },
|
|
262
|
+
u_data: { value: null },
|
|
263
|
+
u_stop_geom: { value: null },
|
|
264
|
+
u_geo_color: { value: null },
|
|
265
|
+
u_window_size: { value: new Vector2(1, 1) },
|
|
266
|
+
u_vol_scale: { value: new Vector3(1, 1, 1) },
|
|
267
|
+
u_physical_Pixel: { value: 0.5 },
|
|
268
|
+
volumeTex: { value: null },
|
|
269
|
+
volumeTex2: { value: null },
|
|
270
|
+
volumeTex3: { value: null },
|
|
271
|
+
volumeTex4: { value: null },
|
|
272
|
+
volumeTex5: { value: null },
|
|
273
|
+
volumeTex6: { value: null },
|
|
274
|
+
u_color: { value: new Vector3(0, 0, 0) },
|
|
275
|
+
u_color2: { value: new Vector3(0, 0, 0) },
|
|
276
|
+
u_color3: { value: new Vector3(0, 0, 0) },
|
|
277
|
+
u_color4: { value: new Vector3(0, 0, 0) },
|
|
278
|
+
u_color5: { value: new Vector3(0, 0, 0) },
|
|
279
|
+
u_color6: { value: new Vector3(0, 0, 0) },
|
|
280
|
+
u_cmdata: { value: null },
|
|
281
|
+
near: { value: 0.1 },
|
|
282
|
+
far: { value: 1e4 },
|
|
283
|
+
alphaScale: { value: 0 },
|
|
284
|
+
dtScale: { value: 1 },
|
|
285
|
+
volumeCount: { value: 0 },
|
|
286
|
+
finalGamma: { value: 0 },
|
|
287
|
+
boxSize: { value: new Vector3(1, 1, 1) }
|
|
288
|
+
},
|
|
289
|
+
vertexShader: [
|
|
290
|
+
"out vec3 rayDirUnnorm;",
|
|
291
|
+
"out vec3 cameraCorrected;",
|
|
292
|
+
"uniform vec3 u_vol_scale;",
|
|
293
|
+
"uniform vec3 u_size;",
|
|
294
|
+
"varying vec3 worldSpaceCoords;",
|
|
295
|
+
"varying vec2 vUv;",
|
|
296
|
+
"varying vec4 glPosition;",
|
|
297
|
+
"uniform highp vec3 boxSize;",
|
|
298
|
+
"void main()",
|
|
299
|
+
"{",
|
|
300
|
+
" worldSpaceCoords = position / boxSize + vec3(0.5, 0.5, 0.5); //move it from [-0.5;0.5] to [0,1]",
|
|
301
|
+
" cameraCorrected = (inverse(modelMatrix) * vec4(cameraPosition, 1.)).xyz;",
|
|
302
|
+
" rayDirUnnorm = position - cameraCorrected;",
|
|
303
|
+
" gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);",
|
|
304
|
+
" glPosition = gl_Position;",
|
|
305
|
+
" vUv = uv;",
|
|
306
|
+
"}"
|
|
307
|
+
].join("\n"),
|
|
308
|
+
fragmentShader: [
|
|
309
|
+
"#include <packing>",
|
|
310
|
+
"precision highp float;",
|
|
311
|
+
" precision mediump sampler3D;",
|
|
312
|
+
"in vec3 rayDirUnnorm;",
|
|
313
|
+
"in vec3 cameraCorrected;",
|
|
314
|
+
"uniform sampler3D volumeTex;",
|
|
315
|
+
"uniform sampler3D volumeTex2;",
|
|
316
|
+
"uniform sampler3D volumeTex3;",
|
|
317
|
+
"uniform sampler3D volumeTex4;",
|
|
318
|
+
"uniform sampler3D volumeTex5;",
|
|
319
|
+
"uniform sampler3D volumeTex6;",
|
|
320
|
+
"uniform vec2 u_clim;",
|
|
321
|
+
"uniform vec2 u_clim2;",
|
|
322
|
+
"uniform vec2 u_clim3;",
|
|
323
|
+
"uniform vec2 u_clim4;",
|
|
324
|
+
"uniform vec2 u_clim5;",
|
|
325
|
+
"uniform vec2 u_clim6;",
|
|
326
|
+
"uniform vec2 u_window_size;",
|
|
327
|
+
"uniform vec2 u_xClip;",
|
|
328
|
+
"uniform vec2 u_yClip;",
|
|
329
|
+
"uniform vec2 u_zClip;",
|
|
330
|
+
"uniform sampler2D u_cmdata;",
|
|
331
|
+
"uniform sampler2D u_stop_geom;",
|
|
332
|
+
"uniform sampler2D u_geo_color;",
|
|
333
|
+
"uniform vec3 u_color;",
|
|
334
|
+
"uniform vec3 u_color2;",
|
|
335
|
+
"uniform vec3 u_color3;",
|
|
336
|
+
"uniform vec3 u_color4;",
|
|
337
|
+
"uniform vec3 u_color5;",
|
|
338
|
+
"uniform vec3 u_color6;",
|
|
339
|
+
"uniform float alphaScale;",
|
|
340
|
+
"uniform float dtScale;",
|
|
341
|
+
"uniform float finalGamma;",
|
|
342
|
+
"uniform float volumeCount;",
|
|
343
|
+
"uniform highp vec3 boxSize;",
|
|
344
|
+
"uniform vec3 u_size;",
|
|
345
|
+
"uniform int u_renderstyle;",
|
|
346
|
+
"uniform float u_opacity;",
|
|
347
|
+
"uniform vec3 u_vol_scale;",
|
|
348
|
+
"uniform float near;",
|
|
349
|
+
"uniform float u_physical_Pixel;",
|
|
350
|
+
"varying vec2 vUv;",
|
|
351
|
+
"varying vec4 glPosition;",
|
|
352
|
+
"uniform float far;",
|
|
353
|
+
"varying vec3 worldSpaceCoords;",
|
|
354
|
+
"float linearize_z(float z) {",
|
|
355
|
+
" return near * far / (far + z * (near - far));",
|
|
356
|
+
"}",
|
|
357
|
+
"vec2 intersect_hit(vec3 orig, vec3 dir) {",
|
|
358
|
+
" vec3 boxMin = vec3(-0.5) * boxSize;",
|
|
359
|
+
" vec3 boxMax = vec3( 0.5) * boxSize;",
|
|
360
|
+
" if(u_xClip.x > -1.0){ boxMin.x = u_xClip.x-(boxSize.x/2.0);",
|
|
361
|
+
" if(u_xClip.y < boxSize.x)",
|
|
362
|
+
" boxMax.x = u_xClip.y-(boxSize.x/2.0);",
|
|
363
|
+
" }",
|
|
364
|
+
" if(u_yClip.x > -1.0){ boxMin.y = u_yClip.x-(boxSize.y/2.0);",
|
|
365
|
+
" if(u_yClip.y < boxSize.y)",
|
|
366
|
+
" boxMax.y = u_yClip.y-(boxSize.y/2.0);",
|
|
367
|
+
" }",
|
|
368
|
+
" if(u_zClip.x > -1.0){ boxMin.z = u_zClip.x-(boxSize.z/2.0);",
|
|
369
|
+
" if(u_zClip.y < boxSize.z) boxMax.z = u_zClip.y-(boxSize.z/2.0);",
|
|
370
|
+
" }",
|
|
371
|
+
" vec3 invDir = 1.0 / dir;",
|
|
372
|
+
" vec3 tmin0 = (boxMin - orig) * invDir;",
|
|
373
|
+
" vec3 tmax0 = (boxMax - orig) * invDir;",
|
|
374
|
+
" vec3 tmin = min(tmin0, tmax0);",
|
|
375
|
+
" vec3 tmax = max(tmin0, tmax0);",
|
|
376
|
+
" float t0 = max(tmin.x, max(tmin.y, tmin.z));",
|
|
377
|
+
" float t1 = min(tmax.x, min(tmax.y, tmax.z));",
|
|
378
|
+
" return vec2(t0, t1);",
|
|
379
|
+
"}",
|
|
380
|
+
" // Pseudo-random number gen from",
|
|
381
|
+
" // http://www.reedbeta.com/blog/quick-and-easy-gpu-random-numbers-in-d3d11/",
|
|
382
|
+
" // with some tweaks for the range of values",
|
|
383
|
+
" float wang_hash(int seed) {",
|
|
384
|
+
" seed = (seed ^ 61) ^ (seed >> 16);",
|
|
385
|
+
" seed *= 9;",
|
|
386
|
+
" seed = seed ^ (seed >> 4);",
|
|
387
|
+
" seed *= 0x27d4eb2d;",
|
|
388
|
+
" seed = seed ^ (seed >> 15);",
|
|
389
|
+
" return float(seed % 2147483647) / float(2147483647);",
|
|
390
|
+
" }",
|
|
391
|
+
"float linear_to_srgb(float x) {",
|
|
392
|
+
" if (x <= 0.0031308f) {",
|
|
393
|
+
" return 12.92f * x;",
|
|
394
|
+
" }",
|
|
395
|
+
" return 1.055f * pow(x, 1.f / 2.4f) - 0.055f;",
|
|
396
|
+
"}",
|
|
397
|
+
"void main(void) {",
|
|
398
|
+
// For finding the settings for the MESH
|
|
399
|
+
// " gl_FragColor = vec4(worldSpaceCoords.x,worldSpaceCoords.y,worldSpaceCoords.z,0.5);",
|
|
400
|
+
// " return;",
|
|
401
|
+
//
|
|
402
|
+
" //STEP 1: Normalize the view Ray",
|
|
403
|
+
" vec3 rayDir = normalize(rayDirUnnorm);",
|
|
404
|
+
" //STEP 2: Intersect the ray with the volume bounds to find the interval along the ray overlapped by the volume",
|
|
405
|
+
" vec2 t_hit = intersect_hit(cameraCorrected, rayDir);",
|
|
406
|
+
" if (t_hit.x >= t_hit.y) {",
|
|
407
|
+
" discard;",
|
|
408
|
+
" }",
|
|
409
|
+
" //No sample behind the eye",
|
|
410
|
+
" t_hit.x = max(t_hit.x, 0.0);",
|
|
411
|
+
" //STEP 3: Compute the step size to march through the volume grid",
|
|
412
|
+
" ivec3 volumeTexSize = textureSize(volumeTex, 0);",
|
|
413
|
+
" vec3 dt_vec = 1.0 / (vec3(volumeTexSize) * abs(rayDir));",
|
|
414
|
+
" float dt = min(dt_vec.x, min(dt_vec.y, dt_vec.z));",
|
|
415
|
+
" dt = max(1.0, dt);",
|
|
416
|
+
" // Ray starting point, in the real space where the box may not be a cube.",
|
|
417
|
+
" // Prevents a lost WebGL context.",
|
|
418
|
+
// " if (dt < 0.0000001) {",
|
|
419
|
+
// " gl_FragColor = vec4(1.0);",
|
|
420
|
+
// " return;",
|
|
421
|
+
// " }",
|
|
422
|
+
" float offset = wang_hash(int(gl_FragCoord.x + 640.0 * gl_FragCoord.y));",
|
|
423
|
+
" vec3 p = cameraCorrected + (t_hit.x + offset + dt) * rayDir;",
|
|
424
|
+
" // Most browsers do not need this initialization, but add it to be safe.",
|
|
425
|
+
" gl_FragColor = vec4(0.0);",
|
|
426
|
+
" p = p / boxSize + vec3(0.5);",
|
|
427
|
+
" vec3 step = (rayDir * dt) / boxSize;",
|
|
428
|
+
" // ",
|
|
429
|
+
" // Initialization of some variables.",
|
|
430
|
+
" float max_val = 0.0;",
|
|
431
|
+
" float max_val2 = 0.0;",
|
|
432
|
+
" float max_val3 = 0.0;",
|
|
433
|
+
" float max_val4 = 0.0;",
|
|
434
|
+
" float max_val5 = 0.0;",
|
|
435
|
+
" float max_val6 = 0.0;",
|
|
436
|
+
" vec3 rgbCombo = vec3(0.0);",
|
|
437
|
+
" float total = 0.0;",
|
|
438
|
+
" int max_i = 30000;",
|
|
439
|
+
" int i = 0;",
|
|
440
|
+
" float x = gl_FragCoord.x/u_window_size.x;",
|
|
441
|
+
" float y = gl_FragCoord.y/u_window_size.y;",
|
|
442
|
+
" vec3 meshPos = texture2D(u_stop_geom, vec2(x,y)).xyz;",
|
|
443
|
+
// " vec3 meshPos = texture2D(u_stop_geom, vec2(gl_FragCoord.x,gl_FragCoord.y)).xyz;",
|
|
444
|
+
// " gl_FragColor = vec4(meshPos,1.0);",
|
|
445
|
+
// " return;",
|
|
446
|
+
" float dist = 1000.0;",
|
|
447
|
+
" for (float t = t_hit.x; t < t_hit.y; t += dt) {",
|
|
448
|
+
" if(meshPos != vec3(0.0)) dist = distance(p,meshPos);",
|
|
449
|
+
" float val = texture(volumeTex, p.xyz).r;",
|
|
450
|
+
" val = max(0.0, (val - u_clim[0]) / (u_clim[1] - u_clim[0]));",
|
|
451
|
+
" rgbCombo += max(0.0, min(1.0, val)) * u_color;",
|
|
452
|
+
" total += val;",
|
|
453
|
+
" if(volumeCount > 1.0){ float val2 = texture(volumeTex2, p.xyz).r;",
|
|
454
|
+
" val2 = max(0.0,(val2 - u_clim2[0]) / (u_clim2[1] - u_clim2[0]));",
|
|
455
|
+
" rgbCombo += max(0.0, min(1.0, val2)) * u_color2;",
|
|
456
|
+
" total += val2;",
|
|
457
|
+
" }",
|
|
458
|
+
" if(volumeCount > 2.0){ float val3 = texture(volumeTex3, p.xyz).r;",
|
|
459
|
+
" val3 = max(0.0,(val3 - u_clim3[0]) / (u_clim3[1] - u_clim3[0]));",
|
|
460
|
+
" rgbCombo += max(0.0, min(1.0, val3)) * u_color3;",
|
|
461
|
+
" total += val3;",
|
|
462
|
+
" }",
|
|
463
|
+
" if(volumeCount > 3.0){ float val4 = texture(volumeTex4, p.xyz).r;",
|
|
464
|
+
" val4 = max(0.0,(val4 - u_clim4[0]) / (u_clim4[1] - u_clim4[0]));",
|
|
465
|
+
" rgbCombo += max(0.0, min(1.0, val4)) * u_color4;",
|
|
466
|
+
" total += val4;",
|
|
467
|
+
" }",
|
|
468
|
+
" if(volumeCount > 4.0){ float val5 = texture(volumeTex5, p.xyz).r;",
|
|
469
|
+
" val5 = max(0.0,(val5 - u_clim5[0]) / (u_clim5[1] - u_clim5[0]));",
|
|
470
|
+
" rgbCombo += max(0.0, min(1.0, val5)) * u_color5;",
|
|
471
|
+
" total += val5;",
|
|
472
|
+
" }",
|
|
473
|
+
" if(volumeCount > 5.0){ float val6 = texture(volumeTex6, p.xyz).r;",
|
|
474
|
+
" val6 = max(0.0,(val6 - u_clim6[0]) / (u_clim6[1] - u_clim6[0]));",
|
|
475
|
+
" rgbCombo += max(0.0, min(1.0, val6)) * u_color6;",
|
|
476
|
+
" total += val6;",
|
|
477
|
+
" }",
|
|
478
|
+
// STOP the traversal if there has been data and the distance to the object is too small
|
|
479
|
+
" if(total > 0.0 && dist < 0.1){",
|
|
480
|
+
" break;",
|
|
481
|
+
" }else if(dist < 0.1){ gl_FragColor = vec4(0.0,0.0,0.0,0.0);",
|
|
482
|
+
" break;",
|
|
483
|
+
" }",
|
|
484
|
+
" if(u_renderstyle == 0 && (max_val > u_clim[1] && max_val2 >= u_clim2[1] && max_val3 >= u_clim3[1] && max_val4 >= u_clim4[1] && max_val5 >= u_clim5[1] && max_val6 >= u_clim6[1])) break;",
|
|
485
|
+
" if(u_renderstyle == 2){ total = min(total, 1.0);",
|
|
486
|
+
" vec4 val_color = vec4(rgbCombo, total);",
|
|
487
|
+
" val_color.a = 1.0 - pow(1.0 - val_color.a, 1.0);",
|
|
488
|
+
" gl_FragColor.rgb += (1.0 - gl_FragColor.a) * val_color.a * val_color.rgb;",
|
|
489
|
+
" gl_FragColor.a += (1.0 - gl_FragColor.a) * val_color.a * dtScale;",
|
|
490
|
+
" if (gl_FragColor.a >= 0.95) {",
|
|
491
|
+
" break;",
|
|
492
|
+
" }",
|
|
493
|
+
" }",
|
|
494
|
+
// " }",
|
|
495
|
+
" p += step;",
|
|
496
|
+
" }",
|
|
497
|
+
" gl_FragDepth = distance(worldSpaceCoords,p)*u_physical_Pixel;",
|
|
498
|
+
// " gl_FragColor = vec4(gl_FragDepth,gl_FragDepth,gl_FragDepth,1.0);",
|
|
499
|
+
// " return;",
|
|
500
|
+
" if(u_renderstyle == 0 && (max_val < u_clim[0] && max_val2 < u_clim2[0] && max_val3 < u_clim3[0] && max_val4 < u_clim4[0] && max_val5 < u_clim5[0] && max_val6 < u_clim6[0])){",
|
|
501
|
+
" gl_FragColor = vec4(0,0,0,0);",
|
|
502
|
+
" }else if(u_renderstyle == 0){",
|
|
503
|
+
" max_val = (max_val - u_clim[0]) / (u_clim[1] - u_clim[0]);",
|
|
504
|
+
" max_val2 = (max_val2 - u_clim2[0]) / (u_clim2[1] - u_clim2[0]);",
|
|
505
|
+
" max_val3 = (max_val3 - u_clim3[0]) / (u_clim3[1] - u_clim3[0]);",
|
|
506
|
+
" max_val4 = (max_val4 - u_clim4[0]) / (u_clim4[1] - u_clim4[0]);",
|
|
507
|
+
" max_val5 = (max_val5 - u_clim5[0]) / (u_clim5[1] - u_clim5[0]);",
|
|
508
|
+
" max_val6 = (max_val6 - u_clim6[0]) / (u_clim6[1] - u_clim6[0]);",
|
|
509
|
+
" vec3 color = u_color * max_val;",
|
|
510
|
+
" if(volumeCount > 1.0) color = color + u_color2 * max_val2;",
|
|
511
|
+
" if(volumeCount > 3.0) color = color + u_color4 * max_val4;",
|
|
512
|
+
" if(volumeCount > 2.0) color = color + u_color3 * max_val3;",
|
|
513
|
+
" if(volumeCount > 4.0) color = color + u_color5 * max_val5;",
|
|
514
|
+
" if(volumeCount > 5.0) color = color + u_color6 * max_val6;",
|
|
515
|
+
" vec3 colorCorrected = vec3(min(color[0], 1.0), min(color[1],1.0), min(color[2],1.0));",
|
|
516
|
+
" gl_FragColor = vec4(color,1.0);",
|
|
517
|
+
" }",
|
|
518
|
+
" gl_FragColor.r = linear_to_srgb(gl_FragColor.r);",
|
|
519
|
+
" gl_FragColor.g = linear_to_srgb(gl_FragColor.g);",
|
|
520
|
+
" gl_FragColor.b = linear_to_srgb(gl_FragColor.b);",
|
|
521
|
+
"}"
|
|
522
|
+
].join("\n")
|
|
523
|
+
};
|
|
524
|
+
const renderingModeMap = {
|
|
525
|
+
maximumIntensityProjection: 0,
|
|
526
|
+
minimumIntensityProjection: 1,
|
|
527
|
+
additive: 2
|
|
528
|
+
};
|
|
529
|
+
function extractInformationFromProps(layerScope, layerCoordination, channelScopes, channelCoordination, image, props) {
|
|
530
|
+
const { spatialRenderingMode } = props;
|
|
531
|
+
const data = image?.image?.instance?.getData();
|
|
532
|
+
if (!data) {
|
|
533
|
+
return {
|
|
534
|
+
channelsVisible: null,
|
|
535
|
+
resolution: null,
|
|
536
|
+
data: null,
|
|
537
|
+
colors: null,
|
|
538
|
+
contrastLimits: null,
|
|
539
|
+
allChannels: null,
|
|
540
|
+
channelTargetC: null,
|
|
541
|
+
is3dMode: false,
|
|
542
|
+
renderingMode: null,
|
|
543
|
+
layerTransparency: 1,
|
|
544
|
+
xSlice: null,
|
|
545
|
+
ySlice: null,
|
|
546
|
+
zSlice: null
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
const imageWrapperInstance = image.image.instance;
|
|
550
|
+
const is3dMode = spatialRenderingMode === "3D";
|
|
551
|
+
const isRgb = layerCoordination[CoordinationType.PHOTOMETRIC_INTERPRETATION] === "RGB";
|
|
552
|
+
const renderingModeStr = layerCoordination[CoordinationType.VOLUMETRIC_RENDERING_ALGORITHM];
|
|
553
|
+
const renderingMode = renderingModeMap[renderingModeStr];
|
|
554
|
+
const visible = layerCoordination[CoordinationType.SPATIAL_LAYER_VISIBLE];
|
|
555
|
+
const layerTransparency = layerCoordination[CoordinationType.SPATIAL_LAYER_OPACITY];
|
|
556
|
+
if (imageWrapperInstance.isInterleaved()) ;
|
|
557
|
+
const colors = isRgb ? [
|
|
558
|
+
[255, 0, 0],
|
|
559
|
+
[0, 255, 0],
|
|
560
|
+
[0, 0, 255]
|
|
561
|
+
] : channelScopes.map((cScope) => channelCoordination[cScope][CoordinationType.SPATIAL_CHANNEL_COLOR]);
|
|
562
|
+
const contrastLimits = isRgb ? [
|
|
563
|
+
[0, 255],
|
|
564
|
+
[0, 255],
|
|
565
|
+
[0, 255]
|
|
566
|
+
] : channelScopes.map((cScope) => channelCoordination[cScope][CoordinationType.SPATIAL_CHANNEL_WINDOW] || [0, 255]);
|
|
567
|
+
const channelsVisible = isRgb ? [
|
|
568
|
+
// Layer visible AND channel visible
|
|
569
|
+
visible && true,
|
|
570
|
+
visible && true,
|
|
571
|
+
visible && true
|
|
572
|
+
] : channelScopes.map((cScope) => (
|
|
573
|
+
// Layer visible AND channel visible
|
|
574
|
+
visible && channelCoordination[cScope][CoordinationType.SPATIAL_CHANNEL_VISIBLE]
|
|
575
|
+
));
|
|
576
|
+
const channelTargetC = isRgb ? [
|
|
577
|
+
// Layer visible AND channel visible
|
|
578
|
+
visible && true,
|
|
579
|
+
visible && true,
|
|
580
|
+
visible && true
|
|
581
|
+
] : channelScopes.map((cScope) => (
|
|
582
|
+
// Layer visible AND channel visible
|
|
583
|
+
visible && imageWrapperInstance.getChannelIndex(channelCoordination[cScope][CoordinationType.SPATIAL_TARGET_C])
|
|
584
|
+
));
|
|
585
|
+
const autoTargetResolution = imageWrapperInstance.getAutoTargetResolution();
|
|
586
|
+
const targetResolution = layerCoordination[CoordinationType.SPATIAL_TARGET_RESOLUTION];
|
|
587
|
+
const resolution = targetResolution === null || Number.isNaN(targetResolution) ? autoTargetResolution : targetResolution;
|
|
588
|
+
const allChannels = image.image.loaders[0].channels ?? [];
|
|
589
|
+
let xSlice = layerCoordination[CoordinationType.SPATIAL_SLICE_X];
|
|
590
|
+
let ySlice = layerCoordination[CoordinationType.SPATIAL_SLICE_Y];
|
|
591
|
+
let zSlice = layerCoordination[CoordinationType.SPATIAL_SLICE_Z];
|
|
592
|
+
xSlice = xSlice !== null ? xSlice : [-1, 1e5];
|
|
593
|
+
ySlice = ySlice !== null ? ySlice : [-1, 1e5];
|
|
594
|
+
zSlice = zSlice !== null ? zSlice : [-1, 1e5];
|
|
595
|
+
return {
|
|
596
|
+
channelsVisible,
|
|
597
|
+
allChannels,
|
|
598
|
+
channelTargetC,
|
|
599
|
+
resolution,
|
|
600
|
+
data,
|
|
601
|
+
colors,
|
|
602
|
+
contrastLimits,
|
|
603
|
+
is3dMode,
|
|
604
|
+
renderingMode,
|
|
605
|
+
layerTransparency,
|
|
606
|
+
xSlice,
|
|
607
|
+
ySlice,
|
|
608
|
+
zSlice
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
function useVolumeSettings(props, volumeSettings, setVolumeSettings, dataReady, setDataReady) {
|
|
612
|
+
const { images = {}, imageLayerScopes, imageLayerCoordination, imageChannelScopesByLayer, imageChannelCoordination } = props;
|
|
613
|
+
const layerScope = imageLayerScopes[0];
|
|
614
|
+
const channelScopes = imageChannelScopesByLayer[layerScope];
|
|
615
|
+
const layerCoordination = imageLayerCoordination[0][layerScope];
|
|
616
|
+
const channelCoordination = imageChannelCoordination[0][layerScope];
|
|
617
|
+
const { channelsVisible, allChannels, channelTargetC, resolution, data, colors, contrastLimits, is3dMode, renderingMode, layerTransparency, xSlice, ySlice, zSlice } = extractInformationFromProps(layerScope, layerCoordination, channelScopes, channelCoordination, images[layerScope], props);
|
|
618
|
+
if (channelTargetC !== null) {
|
|
619
|
+
if (volumeSettings.channelTargetC?.length !== 0 && (volumeSettings.channelTargetC?.toString() !== channelTargetC.toString() || volumeSettings.resolution?.toString() !== resolution.toString())) {
|
|
620
|
+
if (!dataReady)
|
|
621
|
+
setDataReady(true);
|
|
622
|
+
} else if (volumeSettings.channelsVisible?.toString() !== channelsVisible?.toString() || volumeSettings.colors?.toString() !== colors?.toString() || volumeSettings.is3dMode !== is3dMode || volumeSettings.contrastLimits?.toString() !== contrastLimits?.toString() || volumeSettings.renderingMode?.toString() !== renderingMode.toString() || volumeSettings.layerTransparency.toString() !== layerTransparency.toString() || volumeSettings.xSlice?.toString() !== xSlice.toString() || volumeSettings.ySlice?.toString() !== ySlice.toString() || volumeSettings.zSlice?.toString() !== zSlice.toString()) {
|
|
623
|
+
setVolumeSettings({
|
|
624
|
+
channelsVisible,
|
|
625
|
+
allChannels,
|
|
626
|
+
channelTargetC,
|
|
627
|
+
resolution,
|
|
628
|
+
data,
|
|
629
|
+
colors,
|
|
630
|
+
contrastLimits,
|
|
631
|
+
is3dMode,
|
|
632
|
+
renderingMode,
|
|
633
|
+
layerTransparency,
|
|
634
|
+
xSlice,
|
|
635
|
+
ySlice,
|
|
636
|
+
zSlice
|
|
637
|
+
});
|
|
638
|
+
setDataReady(false);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return {
|
|
642
|
+
images,
|
|
643
|
+
layerScope,
|
|
644
|
+
imageLayerScopes,
|
|
645
|
+
imageLayerCoordination,
|
|
646
|
+
imageChannelScopesByLayer,
|
|
647
|
+
imageChannelCoordination,
|
|
648
|
+
channelsVisible,
|
|
649
|
+
allChannels,
|
|
650
|
+
channelTargetC,
|
|
651
|
+
resolution,
|
|
652
|
+
data,
|
|
653
|
+
colors,
|
|
654
|
+
contrastLimits,
|
|
655
|
+
is3dMode,
|
|
656
|
+
renderingMode,
|
|
657
|
+
layerTransparency,
|
|
658
|
+
xSlice,
|
|
659
|
+
ySlice,
|
|
660
|
+
zSlice
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
function getMinMaxValue(value, minMax) {
|
|
664
|
+
const [min, max] = minMax;
|
|
665
|
+
return (value - min) / Math.sqrt(max ** 2 - min ** 2);
|
|
666
|
+
}
|
|
667
|
+
function setUniformsTextures(uniforms, textures, volume, volConfig, renderstyle, contrastLimits, colors, layerTransparency, xSlice, ySlice, zSlice, meshScale, originalScale) {
|
|
668
|
+
uniforms.boxSize.value.set(volume.xLength, volume.yLength, volume.zLength);
|
|
669
|
+
uniforms.volumeTex.value = textures.length > 0 ? textures[0] : null;
|
|
670
|
+
uniforms.volumeTex2.value = textures.length > 1 ? textures[1] : null;
|
|
671
|
+
uniforms.volumeTex3.value = textures.length > 2 ? textures[2] : null;
|
|
672
|
+
uniforms.volumeTex4.value = textures.length > 3 ? textures[3] : null;
|
|
673
|
+
uniforms.volumeTex5.value = textures.length > 4 ? textures[4] : null;
|
|
674
|
+
uniforms.volumeTex6.value = textures.length > 5 ? textures[5] : null;
|
|
675
|
+
uniforms.near.value = 0.1;
|
|
676
|
+
uniforms.far.value = 3e3;
|
|
677
|
+
uniforms.alphaScale.value = 1;
|
|
678
|
+
uniforms.dtScale.value = layerTransparency;
|
|
679
|
+
uniforms.finalGamma.value = 4.5;
|
|
680
|
+
uniforms.volumeCount.value = textures.length;
|
|
681
|
+
uniforms.u_size.value.set(volume.xLength, volume.yLength, volume.zLength);
|
|
682
|
+
uniforms.u_stop_geom.value = null;
|
|
683
|
+
uniforms.u_window_size.value.set(0, 0);
|
|
684
|
+
uniforms.u_vol_scale.value.set(1 / volume.xLength, 1 / volume.yLength, 1 / volume.zLength * 2);
|
|
685
|
+
uniforms.u_renderstyle.value = renderstyle;
|
|
686
|
+
uniforms.u_clim.value.set(contrastLimits.length > 0 ? contrastLimits[0][0] : null, contrastLimits.length > 0 ? contrastLimits[0][1] : null);
|
|
687
|
+
uniforms.u_clim2.value.set(contrastLimits.length > 1 ? contrastLimits[1][0] : null, contrastLimits.length > 1 ? contrastLimits[1][1] : null);
|
|
688
|
+
uniforms.u_clim3.value.set(contrastLimits.length > 2 ? contrastLimits[2][0] : null, contrastLimits.length > 2 ? contrastLimits[2][1] : null);
|
|
689
|
+
uniforms.u_clim4.value.set(contrastLimits.length > 3 ? contrastLimits[3][0] : null, contrastLimits.length > 3 ? contrastLimits[3][1] : null);
|
|
690
|
+
uniforms.u_clim5.value.set(contrastLimits.length > 4 ? contrastLimits[4][0] : null, contrastLimits.length > 4 ? contrastLimits[4][1] : null);
|
|
691
|
+
uniforms.u_clim6.value.set(contrastLimits.length > 5 ? contrastLimits[5][0] : null, contrastLimits.length > 5 ? contrastLimits[5][1] : null);
|
|
692
|
+
uniforms.u_xClip.value.set(xSlice[0] * (1 / meshScale[0]) / originalScale[0] * volume.xLength, xSlice[1] * (1 / meshScale[0]) / originalScale[0] * volume.xLength);
|
|
693
|
+
uniforms.u_yClip.value.set(ySlice[0] * (1 / meshScale[1]) / originalScale[1] * volume.yLength, ySlice[1] * (1 / meshScale[1]) / originalScale[1] * volume.yLength);
|
|
694
|
+
uniforms.u_zClip.value.set(zSlice[0] * (1 / meshScale[2]) / originalScale[2] * volume.zLength, zSlice[1] * (1 / meshScale[1]) / originalScale[2] * volume.zLength);
|
|
695
|
+
uniforms.u_color.value.set(colors.length > 0 ? colors[0][0] : null, colors.length > 0 ? colors[0][1] : null, colors.length > 0 ? colors[0][2] : null);
|
|
696
|
+
uniforms.u_color2.value.set(colors.length > 1 ? colors[1][0] : null, colors.length > 1 ? colors[1][1] : null, colors.length > 1 ? colors[1][2] : null);
|
|
697
|
+
uniforms.u_color3.value.set(colors.length > 2 ? colors[2][0] : null, colors.length > 2 ? colors[2][1] : null, colors.length > 2 ? colors[2][2] : null);
|
|
698
|
+
uniforms.u_color4.value.set(colors.length > 3 ? colors[3][0] : null, colors.length > 3 ? colors[3][1] : null, colors.length > 3 ? colors[3][2] : null);
|
|
699
|
+
uniforms.u_color5.value.set(colors.length > 4 ? colors[4][0] : null, colors.length > 4 ? colors[4][1] : null, colors.length > 4 ? colors[4][2] : null);
|
|
700
|
+
uniforms.u_color6.value.set(colors.length > 5 ? colors[5][0] : null, colors.length > 5 ? colors[5][1] : null, colors.length > 5 ? colors[5][2] : null);
|
|
701
|
+
}
|
|
702
|
+
function create3DRendering(volumes, channelTargetC, channelsVisible, colors, textures, contrastLimits, volumeMinMax, scaleOrUndefined, renderstyle, layerTransparency, xSlice, ySlice, zSlice, originalScale) {
|
|
703
|
+
const texturesList = [];
|
|
704
|
+
const colorsSave = [];
|
|
705
|
+
const contrastLimitsList = [];
|
|
706
|
+
let volume = null;
|
|
707
|
+
let scale = scaleOrUndefined;
|
|
708
|
+
if (scale === void 0 || scale === null || !Array.isArray(scale) || scale.length < 3) {
|
|
709
|
+
scale = [
|
|
710
|
+
{ size: scale?.[0]?.size ?? 1 },
|
|
711
|
+
{ size: scale?.[1]?.size ?? 1 },
|
|
712
|
+
{ size: scale?.[2]?.size ?? 1 }
|
|
713
|
+
];
|
|
714
|
+
} else {
|
|
715
|
+
for (let i = 0; i < scale.length; i++) {
|
|
716
|
+
if (!scale[i] || scale[i].size === void 0 || scale[i].size === null) {
|
|
717
|
+
scale[i] = { size: 1 };
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
channelTargetC.forEach((channel, id) => {
|
|
722
|
+
if (channelsVisible[id]) {
|
|
723
|
+
volume = volumes.get(channel);
|
|
724
|
+
texturesList.push(textures.get(channel));
|
|
725
|
+
colorsSave.push([colors[id][0] / 255, colors[id][1] / 255, colors[id][2] / 255]);
|
|
726
|
+
if (contrastLimits[id][0] === 0 && contrastLimits[id][1] === 255) {
|
|
727
|
+
contrastLimitsList.push([
|
|
728
|
+
getMinMaxValue(volumeMinMax.get(channel)[0], volumeMinMax.get(channel)),
|
|
729
|
+
getMinMaxValue(volumeMinMax.get(channel)[1], volumeMinMax.get(channel))
|
|
730
|
+
]);
|
|
731
|
+
} else {
|
|
732
|
+
contrastLimitsList.push([
|
|
733
|
+
getMinMaxValue(contrastLimits[id][0], volumeMinMax.get(channel)),
|
|
734
|
+
getMinMaxValue(contrastLimits[id][1], volumeMinMax.get(channel))
|
|
735
|
+
]);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
if (volume === null) {
|
|
740
|
+
return null;
|
|
741
|
+
}
|
|
742
|
+
const vol = volume;
|
|
743
|
+
const volconfig = {};
|
|
744
|
+
const shader = VolumeRenderShaderPerspective;
|
|
745
|
+
const uniforms = UniformsUtils.clone(shader.uniforms);
|
|
746
|
+
setUniformsTextures(uniforms, texturesList, vol, volconfig, renderstyle, contrastLimitsList, colorsSave, layerTransparency, xSlice, ySlice, zSlice, [scale[0].size, scale[1].size, scale[2] ? scale[2].size : 1], originalScale);
|
|
747
|
+
return [
|
|
748
|
+
uniforms,
|
|
749
|
+
shader,
|
|
750
|
+
[1, scale[1].size / scale[0].size, scale[2] ? scale[2].size / scale[0].size : 1],
|
|
751
|
+
[vol.xLength, vol.yLength, vol.zLength],
|
|
752
|
+
[1, vol.yLength / vol.xLength, vol.zLength / vol.xLength]
|
|
753
|
+
];
|
|
754
|
+
}
|
|
755
|
+
const dtypeToTypedArray = {
|
|
756
|
+
Uint8: Uint8Array,
|
|
757
|
+
Uint16: Uint16Array,
|
|
758
|
+
Uint32: Uint32Array,
|
|
759
|
+
Int8: Int8Array,
|
|
760
|
+
Int16: Int16Array,
|
|
761
|
+
Int32: Int32Array,
|
|
762
|
+
Float32: Float32Array,
|
|
763
|
+
Float64: Float64Array
|
|
764
|
+
};
|
|
765
|
+
async function getVolumeIntern({
|
|
766
|
+
source,
|
|
767
|
+
selection,
|
|
768
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
769
|
+
onUpdate = () => {
|
|
770
|
+
},
|
|
771
|
+
downsampleDepth = 1,
|
|
772
|
+
signal
|
|
773
|
+
}) {
|
|
774
|
+
const { shape, labels, dtype } = source;
|
|
775
|
+
const { height, width } = getImageSize(source);
|
|
776
|
+
const depth = shape[labels.indexOf("z")];
|
|
777
|
+
const depthDownsampled = Math.max(1, Math.floor(depth / downsampleDepth));
|
|
778
|
+
const rasterSize = height * width;
|
|
779
|
+
const TypedArrayClass = dtypeToTypedArray[dtype];
|
|
780
|
+
const volumeData = new TypedArrayClass(rasterSize * depthDownsampled);
|
|
781
|
+
await Promise.all(new Array(depthDownsampled).fill(0).map(async (_, z) => {
|
|
782
|
+
const depthSelection = {
|
|
783
|
+
...selection,
|
|
784
|
+
z: z * downsampleDepth
|
|
785
|
+
};
|
|
786
|
+
const { data: rasterData } = await source.getRaster({
|
|
787
|
+
selection: depthSelection,
|
|
788
|
+
signal
|
|
789
|
+
});
|
|
790
|
+
let r = 0;
|
|
791
|
+
onUpdate({ z, total: depthDownsampled, progress: 0.5 });
|
|
792
|
+
while (r < rasterSize) {
|
|
793
|
+
const volIndex = z * rasterSize + (rasterSize - r - 1);
|
|
794
|
+
const rasterIndex = (width - r - 1) % width + width * Math.floor(r / width);
|
|
795
|
+
volumeData[volIndex] = rasterData[rasterIndex];
|
|
796
|
+
r += 1;
|
|
797
|
+
}
|
|
798
|
+
onUpdate({ z, total: depthDownsampled, progress: 1 });
|
|
799
|
+
}));
|
|
800
|
+
return {
|
|
801
|
+
data: volumeData,
|
|
802
|
+
height,
|
|
803
|
+
width,
|
|
804
|
+
depth: depthDownsampled
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
function getVolumeByChannel(channel, resolution, loader) {
|
|
808
|
+
return getVolumeIntern({
|
|
809
|
+
source: loader[resolution],
|
|
810
|
+
selection: { t: 0, c: channel },
|
|
811
|
+
// corresponds to the first channel of the first timepoint
|
|
812
|
+
downsampleDepth: 2 ** resolution
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
function getVolumeFromOrigin(volumeOrigin) {
|
|
816
|
+
const volume = new Volume();
|
|
817
|
+
volume.xLength = volumeOrigin.width;
|
|
818
|
+
volume.yLength = volumeOrigin.height;
|
|
819
|
+
volume.zLength = volumeOrigin.depth;
|
|
820
|
+
volume.data = volumeOrigin.data;
|
|
821
|
+
return volume;
|
|
822
|
+
}
|
|
823
|
+
function getData3DTexture(volume) {
|
|
824
|
+
const texture = new Data3DTexture(volume.data, volume.xLength, volume.yLength, volume.zLength);
|
|
825
|
+
texture.format = RedFormat;
|
|
826
|
+
texture.type = FloatType;
|
|
827
|
+
texture.generateMipmaps = false;
|
|
828
|
+
texture.minFilter = LinearFilter;
|
|
829
|
+
texture.magFilter = LinearFilter;
|
|
830
|
+
texture.needsUpdate = true;
|
|
831
|
+
return texture;
|
|
832
|
+
}
|
|
833
|
+
function getPhysicalSizeScalingMatrix(loader) {
|
|
834
|
+
const { x, y, z } = loader?.meta?.physicalSizes ?? {};
|
|
835
|
+
return [{ size: x ?? 1 }, { size: y ?? 1 }, { size: z ?? 1 }];
|
|
836
|
+
}
|
|
837
|
+
function minMaxVolume(volume) {
|
|
838
|
+
const [min, max] = volume.computeMinMax();
|
|
839
|
+
const dataASFloat32 = new Float32Array(volume.data.length);
|
|
840
|
+
for (let i = 0; i < volume.data.length; i++) {
|
|
841
|
+
dataASFloat32[i] = (volume.data[i] - min) / Math.sqrt(max ** 2 - min ** 2);
|
|
842
|
+
}
|
|
843
|
+
return dataASFloat32;
|
|
844
|
+
}
|
|
845
|
+
async function initialDataLoading(channelTargetC, resolution, data, volumes, textures, volumeMinMax, oldResolution) {
|
|
846
|
+
let volume = null;
|
|
847
|
+
let scale = null;
|
|
848
|
+
const { shape, labels } = data[0];
|
|
849
|
+
const channelsToLoad = channelTargetC.filter((channel) => !volumes.has(channel) || resolution !== oldResolution);
|
|
850
|
+
const volumeOrigins = await Promise.all(channelsToLoad.map((channel) => getVolumeByChannel(channel, resolution, data)));
|
|
851
|
+
channelsToLoad.forEach((channel, channelIndex) => {
|
|
852
|
+
const volumeOrigin = volumeOrigins[channelIndex];
|
|
853
|
+
volume = getVolumeFromOrigin(volumeOrigin);
|
|
854
|
+
const minMax = volume.computeMinMax();
|
|
855
|
+
volume.data = minMaxVolume(volume);
|
|
856
|
+
volumes.set(channel, volume);
|
|
857
|
+
textures.set(channel, getData3DTexture(volume));
|
|
858
|
+
volumeMinMax.set(channel, minMax);
|
|
859
|
+
scale = getPhysicalSizeScalingMatrix(data[resolution]);
|
|
860
|
+
});
|
|
861
|
+
return [
|
|
862
|
+
volumes,
|
|
863
|
+
textures,
|
|
864
|
+
volumeMinMax,
|
|
865
|
+
scale,
|
|
866
|
+
[shape[labels.indexOf("x")], shape[labels.indexOf("y")], shape[labels.indexOf("z")]]
|
|
867
|
+
];
|
|
868
|
+
}
|
|
869
|
+
function stringifyLineData(lineData) {
|
|
870
|
+
return `${lineData.startPoint.x},${lineData.startPoint.y},${lineData.startPoint.z};${lineData.endPoint.x},${lineData.endPoint.y},${lineData.endPoint.z}`;
|
|
871
|
+
}
|
|
872
|
+
function isValidGeometrySize(size) {
|
|
873
|
+
if (!Array.isArray(size) || size.length !== 6) {
|
|
874
|
+
return false;
|
|
875
|
+
}
|
|
876
|
+
return size.every((s) => typeof s === "number" || s === void 0);
|
|
877
|
+
}
|
|
878
|
+
function GeometryAndMesh(props) {
|
|
879
|
+
const { segmentationGroup, segmentationSettings, segmentationSceneScale, renderingSettings, materialRef, highlightEntity, setObsHighlight } = props;
|
|
880
|
+
const model = useRef(null);
|
|
881
|
+
const [lines] = useState([]);
|
|
882
|
+
return jsxs("group", { children: [jsxs("group", { children: [segmentationGroup?.visible ? jsxs("group", { children: [jsx("hemisphereLight", { color: 8421504, groundColor: 6316128 }), jsx("directionalLight", { color: 16777215, position: [0, -800, 0] }), jsx("directionalLight", { color: 16777215, position: [0, 800, 0] }), jsx(Bvh, { firstHitOnly: true, children: jsx("primitive", { ref: model, object: segmentationGroup, position: [0, 0, 0], onClick: (e) => {
|
|
883
|
+
if (e.object.parent?.userData.name === "finalPass") {
|
|
884
|
+
highlightEntity(e.object.name, e.object.userData.layerScope, e.object.userData.channelScope);
|
|
885
|
+
}
|
|
886
|
+
}, onPointerOver: (e) => {
|
|
887
|
+
setObsHighlight(e.object.name);
|
|
888
|
+
}, onPointerOut: () => setObsHighlight(null) }) })] }) : null, renderingSettings.uniforms && renderingSettings.shader && renderingSettings.meshScale && renderingSettings.geometrySize ? jsx("group", { children: jsxs("mesh", { scale: renderingSettings.meshScale, ref: materialRef, children: [jsx("boxGeometry", { args: renderingSettings.geometrySize }), jsx("shaderMaterial", { customProgramCacheKey: () => "1", side: FrontSide, uniforms: renderingSettings.uniforms, needsUpdate: true, transparent: true, vertexShader: renderingSettings.shader.vertexShader, fragmentShader: renderingSettings.shader.fragmentShader })] }) }) : null] }), jsx("group", { name: "lines", children: lines.map((object) => jsx(MeasureLine, { currentLine: object, scale: 1 }, stringifyLineData(object))) })] });
|
|
889
|
+
}
|
|
890
|
+
const LazyGeometryAndMeshXR = React__default.lazy(() => import("./GeometryAndMeshXR-Bewsp5O-.js").catch(() => ({ default: GeometryAndMesh })));
|
|
891
|
+
const LazyXRSceneComponents = React__default.lazy(() => import("./XRSceneComponents-DnZLSAJz.js").catch(() => ({ default: () => null })));
|
|
892
|
+
function SpatialThree(props) {
|
|
893
|
+
const materialRef = useRef(null);
|
|
894
|
+
const orbitRef = useRef(null);
|
|
895
|
+
const [initialStartup, setInitialStartup] = useState(false);
|
|
896
|
+
const [dataReady, setDataReady] = useState(false);
|
|
897
|
+
const [segmentationGroup, setSegmentationGroup] = useState(null);
|
|
898
|
+
const [segmentationSceneScale, setSegmentationSceneScale] = useState([1, 1, 1]);
|
|
899
|
+
const [renderingSettings, setRenderingSettings] = useState({
|
|
900
|
+
uniforms: null,
|
|
901
|
+
shader: null,
|
|
902
|
+
meshScale: null,
|
|
903
|
+
geometrySize: null,
|
|
904
|
+
boxSize: null
|
|
905
|
+
});
|
|
906
|
+
const [volumeData, setVolumeData] = useState({
|
|
907
|
+
volumes: /* @__PURE__ */ new Map(),
|
|
908
|
+
textures: /* @__PURE__ */ new Map(),
|
|
909
|
+
volumeMinMax: /* @__PURE__ */ new Map(),
|
|
910
|
+
scale: null,
|
|
911
|
+
resolution: null,
|
|
912
|
+
originalScale: null
|
|
913
|
+
});
|
|
914
|
+
const [volumeSettings, setVolumeSettings] = useState({
|
|
915
|
+
channelsVisible: [],
|
|
916
|
+
allChannels: [],
|
|
917
|
+
channelTargetC: [],
|
|
918
|
+
resolution: null,
|
|
919
|
+
data: null,
|
|
920
|
+
colors: [],
|
|
921
|
+
contrastLimits: [],
|
|
922
|
+
is3dMode: false,
|
|
923
|
+
renderingMode: null,
|
|
924
|
+
layerTransparency: 1
|
|
925
|
+
});
|
|
926
|
+
const [segmentationSettings, setSegmentationSettings] = useState({
|
|
927
|
+
visible: true,
|
|
928
|
+
color: [1, 1, 1],
|
|
929
|
+
opacity: 1,
|
|
930
|
+
multiVisible: "",
|
|
931
|
+
multiOpacity: "",
|
|
932
|
+
multiColor: "",
|
|
933
|
+
data: null,
|
|
934
|
+
obsSets: []
|
|
935
|
+
});
|
|
936
|
+
const { images, layerScope, channelsVisible, allChannels, channelTargetC, resolution, data, colors, contrastLimits, is3dMode, renderingMode, layerTransparency, xSlice, ySlice, zSlice } = useVolumeSettings(props, volumeSettings, setVolumeSettings, dataReady, setDataReady);
|
|
937
|
+
const { obsSegmentations, onEntitySelected, segmentationLayerCoordination, segmentationChannelCoordination, segmentationChannelScopesByLayer } = props;
|
|
938
|
+
let setObsHighlightFct = () => {
|
|
939
|
+
};
|
|
940
|
+
const setsSave = [];
|
|
941
|
+
if (segmentationChannelCoordination[0][layerScope] !== void 0) {
|
|
942
|
+
const segmentationObsSetLayerProps = segmentationChannelCoordination[0][layerScope][layerScope];
|
|
943
|
+
const setters = segmentationChannelCoordination[1][layerScope][layerScope];
|
|
944
|
+
setObsHighlightFct = setters.setObsHighlight;
|
|
945
|
+
const { additionalObsSets: sets } = segmentationObsSetLayerProps;
|
|
946
|
+
if (sets !== null) {
|
|
947
|
+
segmentationObsSetLayerProps.obsSetSelection.forEach((obsSetPath) => {
|
|
948
|
+
const selectedElement = obsSetPath[1];
|
|
949
|
+
sets.tree[0].children.forEach((child) => {
|
|
950
|
+
if (child.name === selectedElement) {
|
|
951
|
+
child.set.forEach(([obsId]) => {
|
|
952
|
+
const info = { name: "", id: "", color: [255, 255, 255] };
|
|
953
|
+
info.name = selectedElement;
|
|
954
|
+
info.id = obsId;
|
|
955
|
+
segmentationObsSetLayerProps.obsSetColor.forEach((color) => {
|
|
956
|
+
if (color.path[1] === selectedElement) {
|
|
957
|
+
info.color = color.color;
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
setsSave.push(info);
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
});
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
if (segmentationObsSetLayerProps.obsHighlight !== null) {
|
|
967
|
+
setsSave.push({ name: "", id: segmentationObsSetLayerProps.obsHighlight, color: [255, 34, 0] });
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
if (obsSegmentations?.[layerScope]?.obsSegmentations && segmentationGroup == null) {
|
|
971
|
+
const { scene, sceneOptions } = obsSegmentations[layerScope].obsSegmentations;
|
|
972
|
+
if (scene?.children) {
|
|
973
|
+
const newScene = new Scene();
|
|
974
|
+
const finalPass = new Group();
|
|
975
|
+
finalPass.userData.name = "finalPass";
|
|
976
|
+
scene.children.forEach((sceneChild) => {
|
|
977
|
+
const childElement = "material" in sceneChild ? sceneChild : sceneChild.children[0];
|
|
978
|
+
if (childElement.material instanceof MeshPhysicalMaterial || childElement.material instanceof MeshBasicMaterial) {
|
|
979
|
+
childElement.material = new MeshStandardMaterial();
|
|
980
|
+
}
|
|
981
|
+
const mat = childElement.material;
|
|
982
|
+
let name = childElement.name.replace("mesh_", "").replace("mesh", "").replace("glb", "").replace("_dec", "").replace("_Decobj", "").replace("obj", "").replace("_DEc", "").replace(".", "").replace("_Dec", "");
|
|
983
|
+
if (name.includes("_")) {
|
|
984
|
+
name = name.split("_")[0];
|
|
985
|
+
}
|
|
986
|
+
childElement.name = name;
|
|
987
|
+
childElement.userData.name = name;
|
|
988
|
+
childElement.userData.layerScope = layerScope;
|
|
989
|
+
mat.transparent = true;
|
|
990
|
+
mat.depthTest = true;
|
|
991
|
+
mat.depthWrite = true;
|
|
992
|
+
mat.needsUpdate = true;
|
|
993
|
+
mat.side = sceneOptions?.materialSide === "back" ? BackSide : FrontSide;
|
|
994
|
+
const simplified = childElement.clone();
|
|
995
|
+
simplified.geometry = childElement.geometry.clone();
|
|
996
|
+
simplified.geometry.translate(sceneOptions?.targetX ?? 0, sceneOptions?.targetY ?? 0, sceneOptions?.targetZ ?? 0);
|
|
997
|
+
simplified.geometry.scale(sceneOptions?.scaleX ?? 1, sceneOptions?.scaleY ?? 1, sceneOptions?.scaleZ ?? 1);
|
|
998
|
+
simplified.geometry.rotateX(sceneOptions?.rotationX ?? 0);
|
|
999
|
+
simplified.geometry.rotateY(sceneOptions?.rotationY ?? 0);
|
|
1000
|
+
simplified.geometry.rotateZ(sceneOptions?.rotationZ ?? 0);
|
|
1001
|
+
const finalPassChild = childElement.clone();
|
|
1002
|
+
finalPassChild.material = mat.clone();
|
|
1003
|
+
finalPassChild.geometry = simplified.geometry.clone();
|
|
1004
|
+
finalPass.add(finalPassChild);
|
|
1005
|
+
});
|
|
1006
|
+
newScene.add(finalPass);
|
|
1007
|
+
newScene.scale.set(sceneOptions?.sceneScaleX ?? 1, sceneOptions?.sceneScaleY ?? 1, sceneOptions?.sceneScaleZ ?? 1);
|
|
1008
|
+
const sceneScale = [
|
|
1009
|
+
sceneOptions?.sceneScaleX ?? 1,
|
|
1010
|
+
sceneOptions?.sceneScaleY ?? 1,
|
|
1011
|
+
sceneOptions?.sceneScaleZ ?? 1
|
|
1012
|
+
];
|
|
1013
|
+
setSegmentationSceneScale(sceneScale);
|
|
1014
|
+
newScene.rotateX(sceneOptions?.sceneRotationX ?? 0);
|
|
1015
|
+
newScene.rotateY(sceneOptions?.sceneRotationY ?? 0);
|
|
1016
|
+
newScene.rotateZ(sceneOptions?.sceneRotationZ ?? 0);
|
|
1017
|
+
setSegmentationGroup(newScene);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
if (segmentationChannelCoordination[0] !== void 0 && segmentationChannelCoordination[0][layerScope] !== void 0) {
|
|
1021
|
+
const segmentationLayerProps = segmentationChannelCoordination[0][layerScope][layerScope];
|
|
1022
|
+
let setsSaveString = "";
|
|
1023
|
+
setsSave.forEach((child) => {
|
|
1024
|
+
setsSaveString += `${child.id};${child.color.toString()};${child.name}`;
|
|
1025
|
+
});
|
|
1026
|
+
let settingsSaveString = "";
|
|
1027
|
+
segmentationSettings.obsSets.forEach((child) => {
|
|
1028
|
+
settingsSaveString += `${child.id};${child.color.toString()};${child.name}`;
|
|
1029
|
+
});
|
|
1030
|
+
if (segmentationChannelScopesByLayer[layerScope].length > 1) {
|
|
1031
|
+
let color = "";
|
|
1032
|
+
let opacity = "";
|
|
1033
|
+
let visible = "";
|
|
1034
|
+
let visibleCombined = false;
|
|
1035
|
+
let opacityCombined = 0;
|
|
1036
|
+
segmentationChannelScopesByLayer[layerScope].forEach((channelScope) => {
|
|
1037
|
+
const channelSet = segmentationChannelCoordination[0][layerScope][channelScope];
|
|
1038
|
+
color += `${channelSet.spatialChannelColor.toString()};`;
|
|
1039
|
+
opacity += `${channelSet.spatialChannelOpacity};`;
|
|
1040
|
+
visible += `${channelSet.spatialChannelVisible};`;
|
|
1041
|
+
visibleCombined = visibleCombined || channelSet.spatialChannelVisible;
|
|
1042
|
+
opacityCombined += channelSet.spatialChannelOpacity;
|
|
1043
|
+
});
|
|
1044
|
+
if (color !== segmentationSettings.multiColor || opacity !== segmentationSettings.multiOpacity || visible !== segmentationSettings.multiVisible) {
|
|
1045
|
+
setSegmentationSettings({
|
|
1046
|
+
color: segmentationLayerProps.spatialChannelColor,
|
|
1047
|
+
opacity: opacityCombined,
|
|
1048
|
+
visible: visibleCombined,
|
|
1049
|
+
multiColor: color,
|
|
1050
|
+
multiVisible: visible,
|
|
1051
|
+
multiOpacity: opacity,
|
|
1052
|
+
data: obsSegmentations ?? null,
|
|
1053
|
+
obsSets: setsSave
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
} else if (segmentationLayerProps.spatialChannelColor.toString() !== segmentationSettings.color.toString() || segmentationLayerProps.spatialChannelVisible !== segmentationSettings.visible || segmentationLayerProps.spatialChannelOpacity !== segmentationSettings.opacity || setsSaveString !== settingsSaveString) {
|
|
1057
|
+
setSegmentationSettings({
|
|
1058
|
+
color: segmentationLayerProps.spatialChannelColor,
|
|
1059
|
+
opacity: segmentationLayerProps.spatialChannelOpacity,
|
|
1060
|
+
visible: segmentationLayerProps.spatialChannelVisible,
|
|
1061
|
+
multiColor: "",
|
|
1062
|
+
multiVisible: "",
|
|
1063
|
+
multiOpacity: "",
|
|
1064
|
+
data: obsSegmentations ?? null,
|
|
1065
|
+
obsSets: setsSave
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
useEffect(() => {
|
|
1070
|
+
if (segmentationGroup !== null) {
|
|
1071
|
+
let firstGroupIndex = 0;
|
|
1072
|
+
let finalGroupIndex = 0;
|
|
1073
|
+
for (let i = 0; i < segmentationGroup.children.length; i++) {
|
|
1074
|
+
if (segmentationGroup.children[i].userData.name === "finalPass") {
|
|
1075
|
+
finalGroupIndex = i;
|
|
1076
|
+
} else {
|
|
1077
|
+
firstGroupIndex = i;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
segmentationGroup.children[finalGroupIndex].children.forEach((sceneChild, childIndex) => {
|
|
1081
|
+
const child = sceneChild;
|
|
1082
|
+
let { color } = segmentationSettings;
|
|
1083
|
+
const id = child.userData.name;
|
|
1084
|
+
segmentationSettings.obsSets.forEach((obsSet) => {
|
|
1085
|
+
if (obsSet.id === id) {
|
|
1086
|
+
color = obsSet.color;
|
|
1087
|
+
}
|
|
1088
|
+
});
|
|
1089
|
+
if (segmentationChannelScopesByLayer[layerScope].length > 1) {
|
|
1090
|
+
segmentationChannelScopesByLayer[layerScope].forEach((channelScope) => {
|
|
1091
|
+
const channelSet = segmentationChannelCoordination[0][layerScope][channelScope];
|
|
1092
|
+
if (channelSet.spatialTargetC === id) {
|
|
1093
|
+
child.material.color.r = channelSet.spatialChannelColor[0] / 255;
|
|
1094
|
+
child.material.color.g = channelSet.spatialChannelColor[1] / 255;
|
|
1095
|
+
child.material.color.b = channelSet.spatialChannelColor[2] / 255;
|
|
1096
|
+
child.material.opacity = channelSet.spatialChannelOpacity;
|
|
1097
|
+
child.visible = channelSet.spatialChannelVisible;
|
|
1098
|
+
child.material.needsUpdate = true;
|
|
1099
|
+
child.userData.layerScope = layerScope;
|
|
1100
|
+
child.userData.channelScope = channelScope;
|
|
1101
|
+
const firstChild = segmentationGroup.children[firstGroupIndex].children[childIndex];
|
|
1102
|
+
firstChild.material.needsUpdate = true;
|
|
1103
|
+
}
|
|
1104
|
+
});
|
|
1105
|
+
} else {
|
|
1106
|
+
child.material.color.r = color[0] / 255;
|
|
1107
|
+
child.material.color.g = color[1] / 255;
|
|
1108
|
+
child.material.color.b = color[2] / 255;
|
|
1109
|
+
child.material.opacity = segmentationSettings.opacity;
|
|
1110
|
+
child.material.visible = segmentationSettings.visible;
|
|
1111
|
+
child.material.needsUpdate = true;
|
|
1112
|
+
child.userData.layerScope = layerScope;
|
|
1113
|
+
const firstChannelScope = Object.keys(segmentationChannelCoordination[0][layerScope])?.[0];
|
|
1114
|
+
child.userData.channelScope = firstChannelScope;
|
|
1115
|
+
}
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
}, [segmentationSettings, segmentationGroup]);
|
|
1119
|
+
const dataToCheck = images[layerScope]?.image?.instance?.getData();
|
|
1120
|
+
if (dataToCheck !== void 0 && !dataReady && !initialStartup && contrastLimits !== null && contrastLimits[0][1] !== 255 && is3dMode) {
|
|
1121
|
+
setDataReady(true);
|
|
1122
|
+
setInitialStartup(true);
|
|
1123
|
+
}
|
|
1124
|
+
useEffect(() => {
|
|
1125
|
+
const fetchRendering = async () => {
|
|
1126
|
+
const loadingResult = await initialDataLoading(channelTargetC, resolution, data, volumeData.volumes, volumeData.textures, volumeData.volumeMinMax, volumeData.resolution);
|
|
1127
|
+
if (loadingResult[0] !== null) {
|
|
1128
|
+
setVolumeData({
|
|
1129
|
+
resolution,
|
|
1130
|
+
volumes: loadingResult[0],
|
|
1131
|
+
textures: loadingResult[1],
|
|
1132
|
+
volumeMinMax: loadingResult[2],
|
|
1133
|
+
scale: loadingResult[3] !== null ? loadingResult[3] : volumeData.scale,
|
|
1134
|
+
originalScale: loadingResult[4]
|
|
1135
|
+
});
|
|
1136
|
+
if (!renderingSettings.uniforms || !renderingSettings.shader) {
|
|
1137
|
+
const rendering = create3DRendering(loadingResult[0], channelTargetC, channelsVisible, colors, loadingResult[1], contrastLimits, loadingResult[2], loadingResult[3], renderingMode, layerTransparency, xSlice, ySlice, zSlice, loadingResult[4]);
|
|
1138
|
+
if (rendering !== null) {
|
|
1139
|
+
setRenderingSettings({
|
|
1140
|
+
uniforms: rendering[0],
|
|
1141
|
+
shader: rendering[1],
|
|
1142
|
+
meshScale: rendering[2],
|
|
1143
|
+
geometrySize: rendering[3],
|
|
1144
|
+
boxSize: rendering[4]
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
} else {
|
|
1148
|
+
setVolumeSettings({
|
|
1149
|
+
channelsVisible,
|
|
1150
|
+
allChannels,
|
|
1151
|
+
channelTargetC,
|
|
1152
|
+
resolution,
|
|
1153
|
+
data,
|
|
1154
|
+
colors,
|
|
1155
|
+
contrastLimits,
|
|
1156
|
+
is3dMode,
|
|
1157
|
+
renderingMode,
|
|
1158
|
+
layerTransparency,
|
|
1159
|
+
xSlice,
|
|
1160
|
+
ySlice,
|
|
1161
|
+
zSlice
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
};
|
|
1166
|
+
if (dataReady) {
|
|
1167
|
+
if (resolution !== volumeSettings.resolution && materialRef.current) {
|
|
1168
|
+
materialRef.current.material.uniforms.volumeCount.value = 0;
|
|
1169
|
+
materialRef.current.material.uniforms.volumeTex.value = null;
|
|
1170
|
+
}
|
|
1171
|
+
fetchRendering();
|
|
1172
|
+
setDataReady(false);
|
|
1173
|
+
}
|
|
1174
|
+
}, [dataReady]);
|
|
1175
|
+
useEffect(() => {
|
|
1176
|
+
if (renderingSettings.uniforms && renderingSettings.shader) {
|
|
1177
|
+
const rendering = create3DRendering(volumeData.volumes, volumeSettings.channelTargetC, volumeSettings.channelsVisible, volumeSettings.colors, volumeData.textures, volumeSettings.contrastLimits, volumeData.volumeMinMax, volumeData.scale, volumeSettings.renderingMode, volumeSettings.layerTransparency, volumeSettings.xSlice, volumeSettings.ySlice, volumeSettings.zSlice, volumeData.originalScale);
|
|
1178
|
+
if (rendering !== null) {
|
|
1179
|
+
let volumeCount = 0;
|
|
1180
|
+
volumeSettings.channelsVisible?.forEach((channelVisible) => {
|
|
1181
|
+
if (channelVisible)
|
|
1182
|
+
volumeCount++;
|
|
1183
|
+
});
|
|
1184
|
+
setDataReady(false);
|
|
1185
|
+
if (materialRef?.current?.material?.uniforms) {
|
|
1186
|
+
materialRef.current.material.uniforms.u_clim.value = rendering[0].u_clim.value;
|
|
1187
|
+
materialRef.current.material.uniforms.u_clim2.value = rendering[0].u_clim2.value;
|
|
1188
|
+
materialRef.current.material.uniforms.u_clim3.value = rendering[0].u_clim3.value;
|
|
1189
|
+
materialRef.current.material.uniforms.u_clim4.value = rendering[0].u_clim4.value;
|
|
1190
|
+
materialRef.current.material.uniforms.u_clim5.value = rendering[0].u_clim5.value;
|
|
1191
|
+
materialRef.current.material.uniforms.u_clim6.value = rendering[0].u_clim6.value;
|
|
1192
|
+
materialRef.current.material.uniforms.u_xClip.value = rendering[0].u_xClip.value;
|
|
1193
|
+
materialRef.current.material.uniforms.u_yClip.value = rendering[0].u_yClip.value;
|
|
1194
|
+
materialRef.current.material.uniforms.u_zClip.value = rendering[0].u_zClip.value;
|
|
1195
|
+
materialRef.current.material.uniforms.u_color.value = rendering[0].u_color.value;
|
|
1196
|
+
materialRef.current.material.uniforms.u_color2.value = rendering[0].u_color2.value;
|
|
1197
|
+
materialRef.current.material.uniforms.u_color3.value = rendering[0].u_color3.value;
|
|
1198
|
+
materialRef.current.material.uniforms.u_color4.value = rendering[0].u_color4.value;
|
|
1199
|
+
materialRef.current.material.uniforms.u_color5.value = rendering[0].u_color5.value;
|
|
1200
|
+
materialRef.current.material.uniforms.u_color6.value = rendering[0].u_color6.value;
|
|
1201
|
+
materialRef.current.material.uniforms.volumeTex.value = rendering[0].volumeTex.value;
|
|
1202
|
+
materialRef.current.material.uniforms.volumeTex2.value = rendering[0].volumeTex2.value;
|
|
1203
|
+
materialRef.current.material.uniforms.volumeTex3.value = rendering[0].volumeTex3.value;
|
|
1204
|
+
materialRef.current.material.uniforms.volumeTex4.value = rendering[0].volumeTex4.value;
|
|
1205
|
+
materialRef.current.material.uniforms.volumeTex5.value = rendering[0].volumeTex5.value;
|
|
1206
|
+
materialRef.current.material.uniforms.volumeTex6.value = rendering[0].volumeTex6.value;
|
|
1207
|
+
materialRef.current.material.uniforms.volumeCount.value = volumeCount;
|
|
1208
|
+
materialRef.current.material.uniforms.u_renderstyle.value = volumeSettings.renderingMode;
|
|
1209
|
+
materialRef.current.material.uniforms.dtScale.value = volumeSettings.layerTransparency;
|
|
1210
|
+
}
|
|
1211
|
+
} else if (materialRef?.current?.material?.uniforms) {
|
|
1212
|
+
materialRef.current.material.uniforms.volumeCount.value = 0;
|
|
1213
|
+
materialRef.current.material.uniforms.volumeTex.value = null;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
}, [volumeSettings]);
|
|
1217
|
+
if (!volumeSettings.is3dMode) {
|
|
1218
|
+
return null;
|
|
1219
|
+
}
|
|
1220
|
+
if (volumeSettings.is3dMode && (!renderingSettings.uniforms || !renderingSettings.shader)) {
|
|
1221
|
+
return jsxs("group", { children: [jsx("ambientLight", {}), jsx("pointLight", { position: [10, 10, 10] }), jsx(Text, { color: "white", scale: 20, fontWeight: 1e3, children: "Loading ..." })] });
|
|
1222
|
+
}
|
|
1223
|
+
const geometryAndMeshProps = {
|
|
1224
|
+
segmentationGroup,
|
|
1225
|
+
segmentationSettings,
|
|
1226
|
+
segmentationSceneScale,
|
|
1227
|
+
renderingSettings,
|
|
1228
|
+
materialRef,
|
|
1229
|
+
highlightEntity: onEntitySelected,
|
|
1230
|
+
setObsHighlight: setObsHighlightFct
|
|
1231
|
+
};
|
|
1232
|
+
const { xrEnabled } = props;
|
|
1233
|
+
return jsxs("group", { children: [xrEnabled ? jsxs(Fragment, { children: [jsx(Suspense, { fallback: null, children: jsx(LazyXRSceneComponents, {}) }), jsx(Suspense, { fallback: jsx(GeometryAndMesh, { ...geometryAndMeshProps }), children: jsx(LazyGeometryAndMeshXR, { ...geometryAndMeshProps }) })] }) : jsx(GeometryAndMesh, { ...geometryAndMeshProps }), jsx(OrbitControls, { ref: orbitRef, enableDamping: false, dampingFactor: 0 })] });
|
|
1234
|
+
}
|
|
1235
|
+
const LazyXRWrapper = React__default.lazy(
|
|
1236
|
+
// eslint-disable-next-line implicit-arrow-linebreak, function-paren-newline
|
|
1237
|
+
() => import("./XRWrapper-C94X82Mb.js").catch(() => ({
|
|
1238
|
+
default: ({ children }) => children
|
|
1239
|
+
}))
|
|
1240
|
+
);
|
|
1241
|
+
const LazyXREnterButton = React__default.lazy(
|
|
1242
|
+
// eslint-disable-next-line implicit-arrow-linebreak, function-paren-newline
|
|
1243
|
+
() => import("./XREnterButton-vmEhdF4L.js").catch(() => ({
|
|
1244
|
+
default: () => null
|
|
1245
|
+
}))
|
|
1246
|
+
);
|
|
1247
|
+
const SpatialWrapper = forwardRef((props, canvasRef) => {
|
|
1248
|
+
const [xrAvailable, setXrAvailable] = useState(false);
|
|
1249
|
+
useEffect(() => {
|
|
1250
|
+
import("@react-three/xr").then(() => setXrAvailable(true)).catch(() => {
|
|
1251
|
+
});
|
|
1252
|
+
}, []);
|
|
1253
|
+
return jsxs("div", { style: { width: "100%", height: "100%" }, children: [xrAvailable && jsx(Suspense, { fallback: null, children: jsx(LazyXREnterButton, {}) }), jsx(Canvas, { style: { position: "absolute", top: 0, left: 0 }, camera: {
|
|
1254
|
+
fov: 50,
|
|
1255
|
+
up: [0, 1, 0],
|
|
1256
|
+
position: [0, 0, 800],
|
|
1257
|
+
near: 0.1,
|
|
1258
|
+
far: 3e3
|
|
1259
|
+
}, gl: { antialias: true, logarithmicDepthBuffer: false }, ref: canvasRef, children: xrAvailable ? jsx(Suspense, { fallback: jsx(SpatialThree, { ...props }), children: jsx(LazyXRWrapper, { children: jsx(SpatialThree, { ...props, xrEnabled: true }) }) }) : jsx(SpatialThree, { ...props }) })] });
|
|
1260
|
+
});
|
|
1261
|
+
const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
1262
|
+
__proto__: null,
|
|
1263
|
+
SpatialWrapper
|
|
1264
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
1265
|
+
export {
|
|
1266
|
+
MeasureLine as M,
|
|
1267
|
+
index as a,
|
|
1268
|
+
isValidGeometrySize as i,
|
|
1269
|
+
stringifyLineData as s
|
|
1270
|
+
};
|