@vitessce/spatial-accelerated 3.8.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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.js +52925 -0
  3. package/dist-tsc/SpatialWrapper.d.ts +2 -0
  4. package/dist-tsc/SpatialWrapper.d.ts.map +1 -0
  5. package/dist-tsc/SpatialWrapper.js +45 -0
  6. package/dist-tsc/VolumeDataManager.d.ts +170 -0
  7. package/dist-tsc/VolumeDataManager.d.ts.map +1 -0
  8. package/dist-tsc/VolumeDataManager.js +1273 -0
  9. package/dist-tsc/VolumeDataManager.test.d.ts +2 -0
  10. package/dist-tsc/VolumeDataManager.test.d.ts.map +1 -0
  11. package/dist-tsc/VolumeDataManager.test.js +164 -0
  12. package/dist-tsc/VolumeRenderManager.d.ts +265 -0
  13. package/dist-tsc/VolumeRenderManager.d.ts.map +1 -0
  14. package/dist-tsc/VolumeRenderManager.js +396 -0
  15. package/dist-tsc/VolumeShader.d.ts +262 -0
  16. package/dist-tsc/VolumeShader.d.ts.map +1 -0
  17. package/dist-tsc/VolumeShader.js +73 -0
  18. package/dist-tsc/VolumeView.d.ts +2 -0
  19. package/dist-tsc/VolumeView.d.ts.map +1 -0
  20. package/dist-tsc/VolumeView.js +521 -0
  21. package/dist-tsc/index.d.ts +2 -0
  22. package/dist-tsc/index.d.ts.map +1 -0
  23. package/dist-tsc/index.js +1 -0
  24. package/dist-tsc/shaders/GaussianFragmentShader.d.ts +2 -0
  25. package/dist-tsc/shaders/GaussianFragmentShader.d.ts.map +1 -0
  26. package/dist-tsc/shaders/GaussianFragmentShader.js +242 -0
  27. package/dist-tsc/shaders/GaussianVertexShader.d.ts +2 -0
  28. package/dist-tsc/shaders/GaussianVertexShader.d.ts.map +1 -0
  29. package/dist-tsc/shaders/GaussianVertexShader.js +51 -0
  30. package/dist-tsc/shaders/VolumeFragmentShader.d.ts +2 -0
  31. package/dist-tsc/shaders/VolumeFragmentShader.d.ts.map +1 -0
  32. package/dist-tsc/shaders/VolumeFragmentShader.js +1817 -0
  33. package/dist-tsc/shaders/VolumeVertexShader.d.ts +2 -0
  34. package/dist-tsc/shaders/VolumeVertexShader.d.ts.map +1 -0
  35. package/dist-tsc/shaders/VolumeVertexShader.js +110 -0
  36. package/package.json +63 -0
  37. package/src/SpatialWrapper.js +60 -0
  38. package/src/VolumeDataManager.js +1515 -0
  39. package/src/VolumeDataManager.test.js +180 -0
  40. package/src/VolumeRenderManager.js +589 -0
  41. package/src/VolumeShader.js +74 -0
  42. package/src/VolumeView.js +651 -0
  43. package/src/index.js +1 -0
  44. package/src/shaders/GaussianFragmentShader.js +243 -0
  45. package/src/shaders/GaussianVertexShader.js +52 -0
  46. package/src/shaders/VolumeFragmentShader.js +1818 -0
  47. package/src/shaders/VolumeVertexShader.js +111 -0
@@ -0,0 +1,589 @@
1
+ /* eslint-disable no-unused-vars */
2
+ import {
3
+ Vector2,
4
+ UniformsUtils,
5
+ } from 'three';
6
+ import { CoordinationType } from '@vitessce/constants-internal';
7
+ import { log, atLeastLogLevel, LogLevel } from '@vitessce/globals';
8
+ import { VolumeShader } from './VolumeShader.js';
9
+
10
+ function logWithColor(message) {
11
+ if (atLeastLogLevel(LogLevel.DEBUG)) {
12
+ console.warn(
13
+ `%cRM: ${message}`,
14
+ 'background: orange; color: white; padding: 2px; border-radius: 3px;',
15
+ );
16
+ }
17
+ }
18
+
19
+ /**
20
+ * Normalize a value based on the volume's min/max range
21
+ * @param {number} value - Value to normalize
22
+ * @param {Array} minMax - [min, max] values of the volume
23
+ * @returns {number} Normalized value
24
+ */
25
+ function normalizeValue(value, minMax) {
26
+ return value / minMax[1];
27
+ }
28
+
29
+ /**
30
+ * Manages the volume rendering state, shader setup, and uniform management
31
+ * Serves as a replacement for many functions from three-utils.js
32
+ */
33
+ export class VolumeRenderManager {
34
+ constructor() {
35
+ logWithColor('Initializing VolumeRenderManager');
36
+ // Rendering settings
37
+ this.uniforms = null;
38
+ this.shader = null;
39
+ this.meshScale = [1, 1, 1];
40
+ this.geometrySize = [1, 1, 1];
41
+ this.boxSize = [1, 1, 1];
42
+ this.zarrInit = false;
43
+
44
+ // Channel and texture state
45
+ this.channelsVisible = [];
46
+ this.channelTargetC = [];
47
+ this.zarrStoreNumResolutions = null;
48
+ this.channelMaxResolutionIndex = [];
49
+ this.colors = [];
50
+ this.contrastLimits = [];
51
+ this.layerTransparency = 1.0;
52
+ this.xSlice = new Vector2(-1, 100000);
53
+ this.ySlice = new Vector2(-1, 100000);
54
+ this.zSlice = new Vector2(-1, 100000);
55
+ this.originalScale = [1, 1, 1];
56
+ this.physicalDimensions = [1, 1, 1]; // can be used later, currently our props scale by pixel
57
+ this.maxResolution = [1, 1, 1];
58
+
59
+ this.maxRange = 255;
60
+ this.maxRangeSet = false;
61
+
62
+ // Additional state
63
+ // this.mrt = null; // Single MRT reference
64
+
65
+ // Initialize shader
66
+ this.initializeShader();
67
+ }
68
+
69
+ /**
70
+ * Initialize shader and uniform objects
71
+ */
72
+ initializeShader() {
73
+ logWithColor('Initializing shader');
74
+ this.shader = VolumeShader;
75
+ this.uniforms = UniformsUtils.clone(this.shader.uniforms);
76
+ }
77
+
78
+ /**
79
+ * Extract rendering settings from coordination props
80
+ * @param {Object} props - Component props
81
+ * @returns {Object} Extracted rendering settings
82
+ */
83
+ extractRenderingSettingsFromProps(props) {
84
+ logWithColor('Extracting rendering settings from props');
85
+ // log.debug('props', props);
86
+ // log.debug('props.imageChannelCoordination', props.imageChannelCoordination[0]['A']);
87
+ const {
88
+ images = {},
89
+ imageLayerScopes = [],
90
+ imageLayerCoordination = [{}],
91
+ imageChannelScopesByLayer = {},
92
+ imageChannelCoordination = [{}],
93
+ spatialRenderingMode,
94
+ } = props;
95
+
96
+ const layerScope = imageLayerScopes[0];
97
+ if (!layerScope) {
98
+ return {
99
+ valid: false,
100
+ };
101
+ }
102
+
103
+ const channelScopes = imageChannelScopesByLayer[layerScope];
104
+ const layerCoordination = imageLayerCoordination[0][layerScope];
105
+ const channelCoordination = imageChannelCoordination[0][layerScope];
106
+
107
+ const data = images[layerScope]?.image?.instance?.getData();
108
+ if (!data) {
109
+ return {
110
+ valid: false,
111
+ };
112
+ }
113
+
114
+
115
+ if (!channelCoordination[channelScopes?.[0]][CoordinationType.SPATIAL_CHANNEL_WINDOW]) {
116
+ // We don't want to initialize with a null spatialChannelWindow,
117
+ // since down below we use the default value of [0, 255], but we may have a Uint16 image.
118
+ // This will cause the sliders to not affect the contrast limits until below 255,
119
+ // but their range will be much larger (e.g., 0-65535).
120
+
121
+ // TODO(mark): the defaults should be set based on the image dtype.
122
+ // TODO(mark): wait for all channel IQR ranges to be computed before initializing,
123
+ // as we do not want this.maxRange to be incorrect.
124
+ return {
125
+ valid: false,
126
+ };
127
+ }
128
+
129
+ const imageWrapperInstance = images[layerScope].image.instance;
130
+ const is3dMode = spatialRenderingMode === '3D';
131
+ const isRgb = layerCoordination[CoordinationType.PHOTOMETRIC_INTERPRETATION] === 'RGB';
132
+ const visible = layerCoordination[CoordinationType.SPATIAL_LAYER_VISIBLE];
133
+ const layerTransparency = layerCoordination[CoordinationType.SPATIAL_LAYER_OPACITY];
134
+
135
+ // Extract colors
136
+ const colors = isRgb ? ([
137
+ [255, 0, 0],
138
+ [0, 255, 0],
139
+ [0, 0, 255],
140
+ ]) : channelScopes.map(cScope => (
141
+ channelCoordination[cScope][CoordinationType.SPATIAL_CHANNEL_COLOR]
142
+ ));
143
+
144
+ // Extract contrast limits
145
+ const contrastLimits = isRgb ? ([
146
+ [0, 255],
147
+ [0, 255],
148
+ [0, 255],
149
+ ]) : channelScopes.map(cScope => (
150
+ channelCoordination[cScope][CoordinationType.SPATIAL_CHANNEL_WINDOW]
151
+ || ([0, 255])
152
+ ));
153
+
154
+ if (!this.maxRangeSet) {
155
+ this.maxRange = Math.max(...contrastLimits.map(limit => limit[1]));
156
+ this.maxRangeSet = true;
157
+ }
158
+
159
+ // Extract channel visibility
160
+ const channelsVisible = isRgb ? ([
161
+ visible && true,
162
+ visible && true,
163
+ visible && true,
164
+ ]) : channelScopes.map(cScope => (
165
+ visible && channelCoordination[cScope][CoordinationType.SPATIAL_CHANNEL_VISIBLE]
166
+ ));
167
+
168
+ // Extract channel target indices
169
+ const channelTargetC = isRgb ? ([
170
+ visible && true,
171
+ visible && true,
172
+ visible && true,
173
+ ]) : channelScopes.map(cScope => (
174
+ visible && imageWrapperInstance.getChannelIndex(
175
+ channelCoordination[cScope][CoordinationType.SPATIAL_TARGET_C],
176
+ )
177
+ ));
178
+
179
+ // Get max resolution index
180
+ const channelMaxResolutionIndex = isRgb ? ([
181
+ visible && null,
182
+ visible && null,
183
+ visible && null,
184
+ ]) : channelScopes.map(cScope => (
185
+ channelCoordination[cScope][CoordinationType.SPATIAL_MAX_RESOLUTION]
186
+ ));
187
+
188
+ // Get slice planes
189
+ let xSlice = layerCoordination[CoordinationType.SPATIAL_SLICE_X];
190
+ let ySlice = layerCoordination[CoordinationType.SPATIAL_SLICE_Y];
191
+ let zSlice = layerCoordination[CoordinationType.SPATIAL_SLICE_Z];
192
+
193
+ xSlice = xSlice !== null ? xSlice : new Vector2(-1, 100000);
194
+ ySlice = ySlice !== null ? ySlice : new Vector2(-1, 100000);
195
+ zSlice = zSlice !== null ? zSlice : new Vector2(-1, 100000);
196
+
197
+ const allChannels = images[layerScope].image.loaders[0].channels;
198
+
199
+ return {
200
+ valid: true,
201
+ channelsVisible,
202
+ allChannels,
203
+ channelTargetC,
204
+ channelMaxResolutionIndex,
205
+ data,
206
+ colors,
207
+ contrastLimits,
208
+ is3dMode,
209
+ layerTransparency,
210
+ xSlice,
211
+ ySlice,
212
+ zSlice,
213
+ };
214
+ }
215
+
216
+ /**
217
+ * Update the render settings from the given props
218
+ * @param {Object} props - Component props
219
+ * @returns {boolean} True if settings were successfully updated
220
+ */
221
+ updateFromProps(props) {
222
+ logWithColor('Updating from props');
223
+ const settings = this.extractRenderingSettingsFromProps(props);
224
+ if (!settings.valid) {
225
+ return false;
226
+ }
227
+
228
+ // Store the extracted settings
229
+ this.channelsVisible = settings.channelsVisible;
230
+ this.channelTargetC = settings.channelTargetC;
231
+ this.channelMaxResolutionIndex = settings.channelMaxResolutionIndex;
232
+ this.colors = settings.colors;
233
+ this.contrastLimits = settings.contrastLimits;
234
+ this.renderingMode = settings.renderingMode;
235
+ this.layerTransparency = settings.layerTransparency;
236
+ this.xSlice = settings.xSlice;
237
+ this.ySlice = settings.ySlice;
238
+ this.zSlice = settings.zSlice;
239
+
240
+ return true;
241
+ }
242
+
243
+ /**
244
+ * Update the rendering based on the current volume data from the data manager
245
+ * @param {object} volumeDataManagerProps - Params derived from the volume data manager
246
+ * @returns {Object|null} Updated rendering settings or null if rendering is not possible
247
+ */
248
+ updateRendering({
249
+ zarrStoreShapes,
250
+ originalScaleXYZ,
251
+ physicalDimensionsXYZ,
252
+ maxResolutionXYZ,
253
+ boxDimensionsXYZ,
254
+ normalizedScaleXYZ,
255
+ bcTHREE,
256
+ ptTHREE,
257
+ }) {
258
+ logWithColor('Updating rendering');
259
+
260
+ // Check if we have at least one visible channel
261
+ const visibleChannelIndex = this.channelTargetC.findIndex(
262
+ (channel, idx) => this.channelsVisible[idx],
263
+ );
264
+
265
+ // if (visibleChannelIndex === -1) {
266
+ // return null;
267
+ // }
268
+
269
+ // Instead of getting dimensions from a volume, get from zarrStore
270
+ if (!Array.isArray(zarrStoreShapes) || zarrStoreShapes.length === 0) {
271
+ return null;
272
+ }
273
+
274
+ // Get dimensions from zarrStore
275
+ const shape = zarrStoreShapes[0];
276
+ // Use the highest resolution shape (shape at index 0)
277
+ // Shape format is typically [t, c, z, y, x]
278
+ const dimensions = {
279
+ xLength: shape[4] || 1,
280
+ yLength: shape[3] || 1,
281
+ zLength: shape[2] || 1,
282
+ };
283
+
284
+ // Collect settings for active channels
285
+ const texturesList = [];
286
+ const colorsSave = [];
287
+ const contrastLimitsList = [];
288
+
289
+ // ', this);
290
+
291
+ // log.debug('this.channelsVisible', this.channelsVisible);
292
+ // log.debug('this.channelTargetC', this.channelTargetC);
293
+ // log.debug('this.colors', this.colors);
294
+ // log.debug('this.contrastLimits', this.contrastLimits);
295
+
296
+ this.channelTargetC.forEach((channel, id) => {
297
+ // log.debug('channel', channel);
298
+ // log.debug('id', id);
299
+ // log.debug('this.channelsVisible', this.channelsVisible);
300
+ if (this.channelsVisible[id] || true) { // TODO: remove true
301
+ // Since we don't have volume-based minMax, use fixed values
302
+ // or get them from your brick cache metadata if available
303
+ // big TODO
304
+ const max = this.maxRange ? this.maxRange : 255;
305
+ const minMax = [0, max]; // Default values
306
+
307
+ // log.debug('this.colors[id]', this.colors[id]);
308
+ // log.debug('this.channelsVisible[id]', this.channelsVisible[id]);
309
+
310
+ colorsSave.push([
311
+ this.colors[id][0] / 255,
312
+ this.colors[id][1] / 255,
313
+ this.colors[id][2] / 255,
314
+ this.channelsVisible[id] ? 1.0 : 0.0,
315
+ ]);
316
+
317
+ log.debug('colorsSave', colorsSave);
318
+
319
+ if (this.contrastLimits[id][0] === 0 && this.contrastLimits[id][1] === 255) {
320
+ contrastLimitsList.push([
321
+ normalizeValue(minMax[0], minMax),
322
+ normalizeValue(minMax[1], minMax),
323
+ ]);
324
+ } else {
325
+ contrastLimitsList.push([
326
+ normalizeValue(this.contrastLimits[id][0], minMax),
327
+ normalizeValue(this.contrastLimits[id][1], minMax),
328
+ ]);
329
+ }
330
+ }
331
+ });
332
+
333
+ if (!this.zarrInit) {
334
+ // Initialize from zarrStore data
335
+ this.originalScale = originalScaleXYZ;
336
+ this.physicalDimensions = physicalDimensionsXYZ;
337
+ this.maxResolution = maxResolutionXYZ;
338
+ const scaledResolution = boxDimensionsXYZ;
339
+ this.normalizedScale = normalizedScaleXYZ;
340
+
341
+ this.meshScale = [
342
+ this.originalScale[0] / this.originalScale[0],
343
+ this.originalScale[1] / this.originalScale[0],
344
+ this.originalScale[2] / this.originalScale[0],
345
+ ];
346
+ this.geometrySize = scaledResolution;
347
+ this.boxSize = scaledResolution;
348
+
349
+ log.debug('this.boxSize', this.boxSize);
350
+ log.debug('this.geometrySize', this.geometrySize);
351
+ log.debug('this.meshScale', this.meshScale);
352
+ log.debug('this.originalScale', this.originalScale);
353
+ log.debug('this.physicalDimensions', this.physicalDimensions);
354
+ log.debug('this.maxResolution', this.maxResolution);
355
+ log.debug('scaledResolution', scaledResolution);
356
+
357
+ this.zarrInit = true;
358
+ }
359
+
360
+ // Update shader uniforms
361
+ this.updateUniforms(
362
+ texturesList,
363
+ dimensions, // Pass dimensions object instead of volume
364
+ this.renderingMode,
365
+ contrastLimitsList,
366
+ colorsSave,
367
+ this.layerTransparency,
368
+ this.xSlice, this.ySlice, this.zSlice,
369
+ bcTHREE,
370
+ ptTHREE,
371
+ );
372
+
373
+ return {
374
+ uniforms: this.uniforms,
375
+ shader: this.shader,
376
+ meshScale: this.meshScale,
377
+ geometrySize: this.geometrySize,
378
+ boxSize: this.boxSize,
379
+ };
380
+ }
381
+
382
+ /**
383
+ * Update shader uniforms with current rendering values
384
+ * @param {Array} textures - List of 3D textures
385
+ * @param {Object} dimensions - Dimensions object
386
+ * @param {number} renderstyle - Rendering mode value
387
+ * @param {Array} contrastLimits - List of contrast limits for each channel
388
+ * @param {Array} colors - List of colors for each channel
389
+ * @param {number} layerTransparency - Overall transparency value
390
+ * @param {Vector2} xSlice - X clipping plane
391
+ * @param {Vector2} ySlice - Y clipping plane
392
+ * @param {Vector2} zSlice - Z clipping plane
393
+ */
394
+ updateUniforms(
395
+ textures, dimensions, renderstyle, contrastLimits, colors, layerTransparency,
396
+ xSlice, ySlice, zSlice,
397
+ brickCacheTexture, pageTableTexture,
398
+ ) {
399
+ logWithColor('Updating uniforms');
400
+ // Set base uniforms
401
+ this.uniforms.boxSize.value.set(this.boxSize[0], this.boxSize[1], this.boxSize[2]);
402
+
403
+ // Set texture uniforms
404
+ this.uniforms.brickCacheTex.value = brickCacheTexture;
405
+ this.uniforms.pageTableTex.value = pageTableTexture;
406
+
407
+ // Set general rendering parameters
408
+ this.uniforms.near.value = 0.1;
409
+ this.uniforms.far.value = 3000; // TODO: check this
410
+ this.uniforms.opacity.value = layerTransparency;
411
+ this.uniforms.volumeCount.value = textures.length;
412
+
413
+ // Set size and scale parameters using dimensions object
414
+ this.uniforms.u_size.value.set(dimensions.xLength, dimensions.yLength, dimensions.zLength);
415
+ this.uniforms.u_window_size.value.set(0, 0);
416
+ this.uniforms.u_vol_scale.value.set(
417
+ 1.0 / dimensions.xLength,
418
+ 1.0 / dimensions.yLength,
419
+ 1.0 / dimensions.zLength * 2.0,
420
+ );
421
+
422
+ // Set contrast limits (up to 6 channels)
423
+ this.uniforms.clim0.value.set(
424
+ contrastLimits.length > 0 ? contrastLimits[0][0] : null,
425
+ contrastLimits.length > 0 ? contrastLimits[0][1] : null,
426
+ );
427
+ this.uniforms.clim1.value.set(
428
+ contrastLimits.length > 1 ? contrastLimits[1][0] : null,
429
+ contrastLimits.length > 1 ? contrastLimits[1][1] : null,
430
+ );
431
+ this.uniforms.clim2.value.set(
432
+ contrastLimits.length > 2 ? contrastLimits[2][0] : null,
433
+ contrastLimits.length > 2 ? contrastLimits[2][1] : null,
434
+ );
435
+ this.uniforms.clim3.value.set(
436
+ contrastLimits.length > 3 ? contrastLimits[3][0] : null,
437
+ contrastLimits.length > 3 ? contrastLimits[3][1] : null,
438
+ );
439
+ this.uniforms.clim4.value.set(
440
+ contrastLimits.length > 4 ? contrastLimits[4][0] : null,
441
+ contrastLimits.length > 4 ? contrastLimits[4][1] : null,
442
+ );
443
+ this.uniforms.clim5.value.set(
444
+ contrastLimits.length > 5 ? contrastLimits[5][0] : null,
445
+ contrastLimits.length > 5 ? contrastLimits[5][1] : null,
446
+ );
447
+ this.uniforms.clim6.value.set(
448
+ contrastLimits.length > 6 ? contrastLimits[6][0] : null,
449
+ contrastLimits.length > 6 ? contrastLimits[6][1] : null,
450
+ );
451
+
452
+ // Set clipping planes
453
+ this.uniforms.xClip.value.set(
454
+ xSlice[0] * (1.0 / this.maxResolution[0]) * this.boxSize[0],
455
+ xSlice[1] * (1.0 / this.maxResolution[0]) * this.boxSize[0],
456
+ );
457
+ this.uniforms.yClip.value.set(
458
+ ySlice[0] * (1.0 / this.maxResolution[1]) * this.boxSize[1],
459
+ ySlice[1] * (1.0 / this.maxResolution[1]) * this.boxSize[1],
460
+ );
461
+ this.uniforms.zClip.value.set(
462
+ zSlice[0] * (1.0 / this.maxResolution[2]) * this.boxSize[2],
463
+ zSlice[1] * (1.0 / this.maxResolution[2]) * this.boxSize[2],
464
+ );
465
+
466
+ // Set colors (up to 7 channels)
467
+ this.uniforms.color0.value.set(
468
+ colors.length > 0 ? colors[0][0] : null,
469
+ colors.length > 0 ? colors[0][1] : null,
470
+ colors.length > 0 ? colors[0][2] : null,
471
+ colors.length > 0 ? colors[0][3] : null,
472
+ );
473
+ this.uniforms.color1.value.set(
474
+ colors.length > 1 ? colors[1][0] : null,
475
+ colors.length > 1 ? colors[1][1] : null,
476
+ colors.length > 1 ? colors[1][2] : null,
477
+ colors.length > 1 ? colors[1][3] : null,
478
+ );
479
+ this.uniforms.color2.value.set(
480
+ colors.length > 2 ? colors[2][0] : null,
481
+ colors.length > 2 ? colors[2][1] : null,
482
+ colors.length > 2 ? colors[2][2] : null,
483
+ colors.length > 2 ? colors[2][3] : null,
484
+ );
485
+ this.uniforms.color3.value.set(
486
+ colors.length > 3 ? colors[3][0] : null,
487
+ colors.length > 3 ? colors[3][1] : null,
488
+ colors.length > 3 ? colors[3][2] : null,
489
+ colors.length > 3 ? colors[3][3] : null,
490
+ );
491
+ this.uniforms.color4.value.set(
492
+ colors.length > 4 ? colors[4][0] : null,
493
+ colors.length > 4 ? colors[4][1] : null,
494
+ colors.length > 4 ? colors[4][2] : null,
495
+ colors.length > 4 ? colors[4][3] : null,
496
+ );
497
+ this.uniforms.color5.value.set(
498
+ colors.length > 5 ? colors[5][0] : null,
499
+ colors.length > 5 ? colors[5][1] : null,
500
+ colors.length > 5 ? colors[5][2] : null,
501
+ colors.length > 5 ? colors[5][3] : null,
502
+ );
503
+ this.uniforms.color6.value.set(
504
+ colors.length > 6 ? colors[6][0] : null,
505
+ colors.length > 6 ? colors[6][1] : null,
506
+ colors.length > 6 ? colors[6][2] : null,
507
+ colors.length > 6 ? colors[6][3] : null,
508
+ );
509
+ for (let i = 0; i < 7; i++) {
510
+ if (typeof this.channelMaxResolutionIndex[i] === 'number') {
511
+ this.uniforms[`res${i}`].value.set(
512
+ Math.max(1, this.channelMaxResolutionIndex[i]), this.zarrStoreNumResolutions - 1,
513
+ );
514
+ }
515
+ }
516
+ }
517
+
518
+ /**
519
+ * Sets the processing render target
520
+ * @param {WebGLMultipleRenderTargets} mrt - Multiple render targets object with 3 attachments
521
+ */
522
+ /*
523
+ setProcessingTargets(mrt) {
524
+ logWithColor('setting processing targets');
525
+ this.mrt = mrt;
526
+ }
527
+ */
528
+
529
+ setChannelMapping(channelMapping) {
530
+ logWithColor('setting channel mapping');
531
+ log.debug('channelMapping', channelMapping);
532
+ this.uniforms.channelMapping.value = channelMapping;
533
+ }
534
+
535
+
536
+ // Only called on initialization of the
537
+ // VolumeDataManager and VolumeRenderManager for a particular image.
538
+ setZarrUniforms(
539
+ zarrStore, PT,
540
+ ) {
541
+ logWithColor('setting zarr uniforms');
542
+ log.debug('zarrStore', zarrStore);
543
+ log.debug('PT', PT);
544
+ for (let i = 0; i <= 9; i++) {
545
+ if (PT.anchors && PT.anchors[i]) {
546
+ this.uniforms[`anchor${i}`].value.set(
547
+ PT.anchors[i][0] || 0,
548
+ PT.anchors[i][1] || 0,
549
+ PT.anchors[i][2] || 0,
550
+ );
551
+ } else {
552
+ // Set default values if anchor doesn't exist
553
+ log.debug('anchor', i, 'does not exist');
554
+ this.uniforms[`anchor${i}`].value.set(0, 0, 0);
555
+ }
556
+ if (zarrStore.scales && zarrStore.scales[i]) {
557
+ this.uniforms[`scale${i}`].value.set(
558
+ zarrStore.scales[i][0] || 1,
559
+ zarrStore.scales[i][1] || 1,
560
+ zarrStore.scales[i][2] || 1,
561
+ );
562
+ } else {
563
+ log.debug('scale', i, 'does not exist');
564
+ }
565
+ }
566
+ log.debug('zarrStore.brickLayout', zarrStore.brickLayout);
567
+ this.zarrStoreNumResolutions = zarrStore.brickLayout.length;
568
+ for (let i = 0; i < 7; i++) {
569
+ this.uniforms[`res${i}`].value.set(
570
+ // TODO(mark) make this dependent on the per-channel maxResolution slider value.
571
+ 1, zarrStore.brickLayout.length - 1,
572
+ );
573
+ }
574
+ this.uniforms.resGlobal.value.set(
575
+ 1, zarrStore.brickLayout.length - 1,
576
+ );
577
+ this.uniforms.voxelExtents.value.set(
578
+ zarrStore.shapes[0][4],
579
+ zarrStore.shapes[0][3],
580
+ zarrStore.shapes[0][2],
581
+ );
582
+ this.uniforms.maxChannels.value = Math.min(zarrStore.channelCount, 7);
583
+ log.debug('this.channelsVisible', this.channelsVisible);
584
+ log.debug('zarrStore.shapes[0]', zarrStore.shapes[0]);
585
+ log.debug('PT', PT);
586
+ log.debug('uniforms', this.uniforms);
587
+ // this.uniforms.physicalScale.value.set(physicalScale);
588
+ }
589
+ }
@@ -0,0 +1,74 @@
1
+ import { Vector2, Vector3, Vector4 } from 'three';
2
+ import { volumeVertexShader } from './shaders/VolumeVertexShader.js';
3
+ import { volumeFragmentShader } from './shaders/VolumeFragmentShader.js';
4
+
5
+ export const VolumeShader = {
6
+ uniforms: {
7
+ u_size: { value: new Vector3(1, 1, 1) },
8
+ clim0: { value: new Vector2(0.2, 0.8) },
9
+ clim1: { value: new Vector2(0.2, 0.8) },
10
+ clim2: { value: new Vector2(0.2, 0.8) },
11
+ clim3: { value: new Vector2(0.2, 0.8) },
12
+ clim4: { value: new Vector2(0.2, 0.8) },
13
+ clim5: { value: new Vector2(0.2, 0.8) },
14
+ clim6: { value: new Vector2(0.2, 0.8) },
15
+ clim7: { value: new Vector2(0.2, 0.8) },
16
+ xClip: { value: new Vector2(0, 1000000.0) },
17
+ yClip: { value: new Vector2(0, 1000000.0) },
18
+ zClip: { value: new Vector2(0, 1000000.0) },
19
+ u_window_size: { value: new Vector2(1, 1) },
20
+ u_vol_scale: { value: new Vector3(1, 1, 1) },
21
+ u_renderstyle: { value: 2 },
22
+ brickCacheTex: { type: 'sampler3D', value: null },
23
+ pageTableTex: { type: 'usampler3D', value: null },
24
+ color0: { value: new Vector4(0, 0, 0) },
25
+ color1: { value: new Vector4(0, 0, 0) },
26
+ color2: { value: new Vector4(0, 0, 0) },
27
+ color3: { value: new Vector4(0, 0, 0) },
28
+ color4: { value: new Vector4(0, 0, 0) },
29
+ color5: { value: new Vector4(0, 0, 0) },
30
+ color6: { value: new Vector4(0, 0, 0) },
31
+ channelMapping: {
32
+ value: [-1, -1, -1, -1, -1, -1, -1],
33
+ },
34
+ resGlobal: { value: new Vector2(0, 9) },
35
+ res0: { value: new Vector2(0, 9) },
36
+ res1: { value: new Vector2(0, 9) },
37
+ res2: { value: new Vector2(0, 9) },
38
+ res3: { value: new Vector2(0, 9) },
39
+ res4: { value: new Vector2(0, 9) },
40
+ res5: { value: new Vector2(0, 9) },
41
+ res6: { value: new Vector2(0, 9) },
42
+ maxChannels: { value: 0 },
43
+ lodFactor: { value: 1.0 },
44
+ near: { value: 0.1 },
45
+ far: { value: 10000 },
46
+ opacity: { value: 1 },
47
+ volumeCount: { value: 0 },
48
+ boxSize: { value: new Vector3(1, 1, 1) },
49
+ renderRes: { value: 1000 },
50
+ voxelExtents: { value: new Vector3(1, 1, 1) },
51
+ anchor0: { value: new Vector3(0, 0, 0) },
52
+ anchor1: { value: new Vector3(0, 0, 0) },
53
+ anchor2: { value: new Vector3(0, 0, 0) },
54
+ anchor3: { value: new Vector3(0, 0, 0) },
55
+ anchor4: { value: new Vector3(0, 0, 0) },
56
+ anchor5: { value: new Vector3(0, 0, 0) },
57
+ anchor6: { value: new Vector3(0, 0, 0) },
58
+ anchor7: { value: new Vector3(0, 0, 0) },
59
+ anchor8: { value: new Vector3(0, 0, 0) },
60
+ anchor9: { value: new Vector3(0, 0, 0) },
61
+ scale0: { value: new Vector3(1, 1, 1) },
62
+ scale1: { value: new Vector3(2, 2, 2) },
63
+ scale2: { value: new Vector3(4, 4, 4) },
64
+ scale3: { value: new Vector3(8, 8, 8) },
65
+ scale4: { value: new Vector3(16, 16, 16) },
66
+ scale5: { value: new Vector3(32, 32, 32) },
67
+ scale6: { value: new Vector3(64, 64, 64) },
68
+ scale7: { value: new Vector3(128, 128, 128) },
69
+ scale8: { value: new Vector3(256, 256, 256) },
70
+ scale9: { value: new Vector3(512, 512, 512) },
71
+ },
72
+ vertexShader: volumeVertexShader,
73
+ fragmentShader: volumeFragmentShader,
74
+ };