@vitessce/all 4.0.0-test.1 → 4.0.0-test.2

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