@cornerstonejs/core 1.53.0 → 1.54.1

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 (183) hide show
  1. package/dist/cjs/RenderingEngine/RenderingEngine.js +5 -0
  2. package/dist/cjs/RenderingEngine/RenderingEngine.js.map +1 -1
  3. package/dist/cjs/RenderingEngine/Viewport.d.ts +1 -2
  4. package/dist/cjs/RenderingEngine/Viewport.js +21 -8
  5. package/dist/cjs/RenderingEngine/Viewport.js.map +1 -1
  6. package/dist/cjs/RenderingEngine/VolumeViewport.d.ts +14 -2
  7. package/dist/cjs/RenderingEngine/VolumeViewport.js +60 -12
  8. package/dist/cjs/RenderingEngine/VolumeViewport.js.map +1 -1
  9. package/dist/cjs/RenderingEngine/VolumeViewport3D.d.ts +0 -1
  10. package/dist/cjs/RenderingEngine/VolumeViewport3D.js +0 -7
  11. package/dist/cjs/RenderingEngine/VolumeViewport3D.js.map +1 -1
  12. package/dist/cjs/RenderingEngine/helpers/setDefaultVolumeVOI.js +10 -7
  13. package/dist/cjs/RenderingEngine/helpers/setDefaultVolumeVOI.js.map +1 -1
  14. package/dist/cjs/cache/cache.d.ts +2 -0
  15. package/dist/cjs/cache/cache.js +10 -0
  16. package/dist/cjs/cache/cache.js.map +1 -1
  17. package/dist/cjs/cache/classes/ImageVolume.d.ts +1 -0
  18. package/dist/cjs/cache/classes/ImageVolume.js +25 -3
  19. package/dist/cjs/cache/classes/ImageVolume.js.map +1 -1
  20. package/dist/cjs/cache/classes/Surface.d.ts +6 -3
  21. package/dist/cjs/cache/classes/Surface.js +9 -0
  22. package/dist/cjs/cache/classes/Surface.js.map +1 -1
  23. package/dist/cjs/cache/index.d.ts +2 -1
  24. package/dist/cjs/cache/index.js +3 -1
  25. package/dist/cjs/cache/index.js.map +1 -1
  26. package/dist/cjs/constants/backgroundColors.d.ts +4 -0
  27. package/dist/cjs/constants/backgroundColors.js +7 -0
  28. package/dist/cjs/constants/backgroundColors.js.map +1 -0
  29. package/dist/cjs/constants/index.d.ts +2 -1
  30. package/dist/cjs/constants/index.js +3 -1
  31. package/dist/cjs/constants/index.js.map +1 -1
  32. package/dist/cjs/enums/Events.d.ts +2 -1
  33. package/dist/cjs/enums/Events.js +1 -0
  34. package/dist/cjs/enums/Events.js.map +1 -1
  35. package/dist/cjs/index.d.ts +2 -2
  36. package/dist/cjs/index.js +2 -1
  37. package/dist/cjs/index.js.map +1 -1
  38. package/dist/cjs/loaders/volumeLoader.d.ts +10 -2
  39. package/dist/cjs/loaders/volumeLoader.js +49 -29
  40. package/dist/cjs/loaders/volumeLoader.js.map +1 -1
  41. package/dist/cjs/requestPool/requestPoolManager.js +1 -1
  42. package/dist/cjs/requestPool/requestPoolManager.js.map +1 -1
  43. package/dist/cjs/types/IGeometry.d.ts +2 -2
  44. package/dist/cjs/types/IImageVolume.d.ts +2 -1
  45. package/dist/cjs/types/ISurface.d.ts +13 -0
  46. package/dist/cjs/types/ISurface.js +3 -0
  47. package/dist/cjs/types/ISurface.js.map +1 -0
  48. package/dist/cjs/types/index.d.ts +2 -1
  49. package/dist/cjs/utilities/VoxelManager.d.ts +1 -1
  50. package/dist/cjs/utilities/VoxelManager.js +3 -0
  51. package/dist/cjs/utilities/VoxelManager.js.map +1 -1
  52. package/dist/cjs/utilities/cacheUtils.js +16 -1
  53. package/dist/cjs/utilities/cacheUtils.js.map +1 -1
  54. package/dist/cjs/utilities/convertVolumeToStackViewport.js +1 -1
  55. package/dist/cjs/utilities/convertVolumeToStackViewport.js.map +1 -1
  56. package/dist/cjs/utilities/generateVolumePropsFromImageIds.js.map +1 -1
  57. package/dist/cjs/utilities/getViewportImageIds.d.ts +3 -0
  58. package/dist/cjs/utilities/getViewportImageIds.js +20 -0
  59. package/dist/cjs/utilities/getViewportImageIds.js.map +1 -0
  60. package/dist/cjs/utilities/index.d.ts +2 -1
  61. package/dist/cjs/utilities/index.js +3 -1
  62. package/dist/cjs/utilities/index.js.map +1 -1
  63. package/dist/cjs/webWorkerManager/webWorkerManager.d.ts +3 -3
  64. package/dist/cjs/webWorkerManager/webWorkerManager.js +40 -30
  65. package/dist/cjs/webWorkerManager/webWorkerManager.js.map +1 -1
  66. package/dist/esm/RenderingEngine/RenderingEngine.js +5 -0
  67. package/dist/esm/RenderingEngine/RenderingEngine.js.map +1 -1
  68. package/dist/esm/RenderingEngine/Viewport.js +21 -8
  69. package/dist/esm/RenderingEngine/Viewport.js.map +1 -1
  70. package/dist/esm/RenderingEngine/VolumeViewport.js +61 -13
  71. package/dist/esm/RenderingEngine/VolumeViewport.js.map +1 -1
  72. package/dist/esm/RenderingEngine/VolumeViewport3D.js +0 -7
  73. package/dist/esm/RenderingEngine/VolumeViewport3D.js.map +1 -1
  74. package/dist/esm/RenderingEngine/helpers/setDefaultVolumeVOI.js +7 -7
  75. package/dist/esm/RenderingEngine/helpers/setDefaultVolumeVOI.js.map +1 -1
  76. package/dist/esm/cache/cache.js +10 -0
  77. package/dist/esm/cache/cache.js.map +1 -1
  78. package/dist/esm/cache/classes/ImageVolume.js +24 -2
  79. package/dist/esm/cache/classes/ImageVolume.js.map +1 -1
  80. package/dist/esm/cache/classes/Surface.js +9 -0
  81. package/dist/esm/cache/classes/Surface.js.map +1 -1
  82. package/dist/esm/cache/index.js +2 -1
  83. package/dist/esm/cache/index.js.map +1 -1
  84. package/dist/esm/constants/backgroundColors.js +5 -0
  85. package/dist/esm/constants/backgroundColors.js.map +1 -0
  86. package/dist/esm/constants/index.js +2 -1
  87. package/dist/esm/constants/index.js.map +1 -1
  88. package/dist/esm/enums/Events.js +1 -0
  89. package/dist/esm/enums/Events.js.map +1 -1
  90. package/dist/esm/index.js +2 -2
  91. package/dist/esm/index.js.map +1 -1
  92. package/dist/esm/loaders/volumeLoader.js +45 -28
  93. package/dist/esm/loaders/volumeLoader.js.map +1 -1
  94. package/dist/esm/requestPool/requestPoolManager.js +1 -1
  95. package/dist/esm/requestPool/requestPoolManager.js.map +1 -1
  96. package/dist/esm/types/ISurface.js +2 -0
  97. package/dist/esm/types/ISurface.js.map +1 -0
  98. package/dist/esm/utilities/VoxelManager.js +3 -0
  99. package/dist/esm/utilities/VoxelManager.js.map +1 -1
  100. package/dist/esm/utilities/cacheUtils.js +15 -1
  101. package/dist/esm/utilities/cacheUtils.js.map +1 -1
  102. package/dist/esm/utilities/convertVolumeToStackViewport.js +1 -1
  103. package/dist/esm/utilities/convertVolumeToStackViewport.js.map +1 -1
  104. package/dist/esm/utilities/generateVolumePropsFromImageIds.js.map +1 -1
  105. package/dist/esm/utilities/getViewportImageIds.js +15 -0
  106. package/dist/esm/utilities/getViewportImageIds.js.map +1 -0
  107. package/dist/esm/utilities/index.js +2 -1
  108. package/dist/esm/utilities/index.js.map +1 -1
  109. package/dist/esm/webWorkerManager/webWorkerManager.js +40 -30
  110. package/dist/esm/webWorkerManager/webWorkerManager.js.map +1 -1
  111. package/dist/types/RenderingEngine/RenderingEngine.d.ts.map +1 -1
  112. package/dist/types/RenderingEngine/Viewport.d.ts +1 -2
  113. package/dist/types/RenderingEngine/Viewport.d.ts.map +1 -1
  114. package/dist/types/RenderingEngine/VolumeViewport.d.ts +14 -2
  115. package/dist/types/RenderingEngine/VolumeViewport.d.ts.map +1 -1
  116. package/dist/types/RenderingEngine/VolumeViewport3D.d.ts +0 -1
  117. package/dist/types/RenderingEngine/VolumeViewport3D.d.ts.map +1 -1
  118. package/dist/types/RenderingEngine/helpers/setDefaultVolumeVOI.d.ts.map +1 -1
  119. package/dist/types/cache/cache.d.ts +2 -0
  120. package/dist/types/cache/cache.d.ts.map +1 -1
  121. package/dist/types/cache/classes/ImageVolume.d.ts +1 -0
  122. package/dist/types/cache/classes/ImageVolume.d.ts.map +1 -1
  123. package/dist/types/cache/classes/Surface.d.ts +6 -3
  124. package/dist/types/cache/classes/Surface.d.ts.map +1 -1
  125. package/dist/types/cache/index.d.ts +2 -1
  126. package/dist/types/cache/index.d.ts.map +1 -1
  127. package/dist/types/constants/backgroundColors.d.ts +5 -0
  128. package/dist/types/constants/backgroundColors.d.ts.map +1 -0
  129. package/dist/types/constants/index.d.ts +2 -1
  130. package/dist/types/constants/index.d.ts.map +1 -1
  131. package/dist/types/enums/Events.d.ts +2 -1
  132. package/dist/types/enums/Events.d.ts.map +1 -1
  133. package/dist/types/index.d.ts +2 -2
  134. package/dist/types/index.d.ts.map +1 -1
  135. package/dist/types/loaders/volumeLoader.d.ts +10 -2
  136. package/dist/types/loaders/volumeLoader.d.ts.map +1 -1
  137. package/dist/types/requestPool/requestPoolManager.d.ts.map +1 -1
  138. package/dist/types/types/IGeometry.d.ts +2 -2
  139. package/dist/types/types/IGeometry.d.ts.map +1 -1
  140. package/dist/types/types/IImageVolume.d.ts +2 -1
  141. package/dist/types/types/IImageVolume.d.ts.map +1 -1
  142. package/dist/types/types/ISurface.d.ts +14 -0
  143. package/dist/types/types/ISurface.d.ts.map +1 -0
  144. package/dist/types/types/index.d.ts +2 -1
  145. package/dist/types/types/index.d.ts.map +1 -1
  146. package/dist/types/utilities/VoxelManager.d.ts +1 -1
  147. package/dist/types/utilities/VoxelManager.d.ts.map +1 -1
  148. package/dist/types/utilities/generateVolumePropsFromImageIds.d.ts.map +1 -1
  149. package/dist/types/utilities/getViewportImageIds.d.ts +4 -0
  150. package/dist/types/utilities/getViewportImageIds.d.ts.map +1 -0
  151. package/dist/types/utilities/index.d.ts +2 -1
  152. package/dist/types/utilities/index.d.ts.map +1 -1
  153. package/dist/types/webWorkerManager/webWorkerManager.d.ts +3 -3
  154. package/dist/types/webWorkerManager/webWorkerManager.d.ts.map +1 -1
  155. package/dist/umd/index.js +1 -1
  156. package/dist/umd/index.js.map +1 -1
  157. package/package.json +2 -2
  158. package/src/RenderingEngine/RenderingEngine.ts +8 -0
  159. package/src/RenderingEngine/Viewport.ts +35 -10
  160. package/src/RenderingEngine/VolumeViewport.ts +135 -16
  161. package/src/RenderingEngine/VolumeViewport3D.ts +0 -8
  162. package/src/RenderingEngine/helpers/setDefaultVolumeVOI.ts +17 -12
  163. package/src/cache/cache.ts +25 -0
  164. package/src/cache/classes/ImageVolume.ts +37 -4
  165. package/src/cache/classes/Surface.ts +16 -4
  166. package/src/cache/index.ts +2 -1
  167. package/src/constants/backgroundColors.ts +5 -0
  168. package/src/constants/index.ts +2 -0
  169. package/src/enums/Events.ts +8 -0
  170. package/src/index.ts +2 -1
  171. package/src/loaders/volumeLoader.ts +109 -43
  172. package/src/requestPool/requestPoolManager.ts +7 -1
  173. package/src/types/IGeometry.ts +2 -2
  174. package/src/types/IImageVolume.ts +9 -1
  175. package/src/types/ISurface.ts +14 -0
  176. package/src/types/index.ts +2 -0
  177. package/src/utilities/VoxelManager.ts +7 -1
  178. package/src/utilities/cacheUtils.ts +25 -1
  179. package/src/utilities/convertVolumeToStackViewport.ts +1 -1
  180. package/src/utilities/generateVolumePropsFromImageIds.ts +1 -2
  181. package/src/utilities/getViewportImageIds.ts +22 -0
  182. package/src/utilities/index.ts +2 -0
  183. package/src/webWorkerManager/webWorkerManager.js +71 -43
@@ -45,12 +45,19 @@ interface DerivedVolumeOptions {
45
45
  };
46
46
  }
47
47
  interface LocalVolumeOptions {
48
- scalarData: PixelDataTypedArray;
49
48
  metadata: Metadata;
50
49
  dimensions: Point3;
51
50
  spacing: Point3;
52
51
  origin: Point3;
53
52
  direction: Mat3;
53
+ scalarData?: PixelDataTypedArray;
54
+ imageIds?: Array<string>;
55
+ referencedImageIds?: Array<string>;
56
+ referencedVolumeId?: string;
57
+ targetBuffer?: {
58
+ type: PixelDataTypedArrayString;
59
+ sharedArrayBuffer?: boolean;
60
+ };
54
61
  }
55
62
 
56
63
  /**
@@ -295,32 +302,11 @@ export async function createAndCacheDerivedVolume(
295
302
  const scalarData = referencedVolume.getScalarData();
296
303
  const scalarLength = scalarData.length;
297
304
 
298
- const { useNorm16Texture } = getConfiguration().rendering;
299
-
300
- // If target buffer is provided
301
- const { TypedArrayConstructor, numBytes } = getBufferConfiguration(
302
- targetBuffer?.type,
303
- scalarLength,
304
- {
305
- use16BitTexture: useNorm16Texture,
306
- isVolumeBuffer: true,
307
- }
305
+ const { volumeScalarData, numBytes } = generateVolumeScalarData(
306
+ targetBuffer,
307
+ scalarLength
308
308
  );
309
309
 
310
- // check if there is enough space in unallocated + image Cache
311
- const isCacheable = cache.isCacheable(numBytes);
312
- if (!isCacheable) {
313
- throw new Error(Events.CACHE_SIZE_EXCEEDED);
314
- }
315
-
316
- let volumeScalarData;
317
- if (targetBuffer?.sharedArrayBuffer) {
318
- const buffer = new SharedArrayBuffer(numBytes);
319
- volumeScalarData = new TypedArrayConstructor(buffer);
320
- } else {
321
- volumeScalarData = new TypedArrayConstructor(scalarLength);
322
- }
323
-
324
310
  // Todo: handle more than one component for segmentation (RGB)
325
311
  const scalarArray = vtkDataArray.newInstance({
326
312
  name: 'Pixels',
@@ -346,7 +332,6 @@ export async function createAndCacheDerivedVolume(
346
332
  imageData: derivedImageData,
347
333
  scalarData: volumeScalarData,
348
334
  sizeInBytes: numBytes,
349
- referencedVolumeId,
350
335
  imageIds: [],
351
336
  });
352
337
 
@@ -374,21 +359,35 @@ export function createLocalVolume(
374
359
  volumeId: string,
375
360
  preventCache = false
376
361
  ): IImageVolume {
377
- const { scalarData, metadata, dimensions, spacing, origin, direction } =
362
+ const { metadata, dimensions, spacing, origin, direction, targetBuffer } =
378
363
  options;
379
364
 
380
- if (
381
- !scalarData ||
382
- !(
383
- scalarData instanceof Uint8Array ||
384
- scalarData instanceof Float32Array ||
385
- scalarData instanceof Uint16Array ||
386
- scalarData instanceof Int16Array
387
- )
388
- ) {
389
- throw new Error(
390
- 'To use createLocalVolume you should pass scalarData of type Uint8Array, Uint16Array, Int16Array or Float32Array'
391
- );
365
+ let { scalarData } = options;
366
+
367
+ // Define the valid data types for scalarData
368
+ const validDataTypes = [
369
+ 'Uint8Array',
370
+ 'Float32Array',
371
+ 'Uint16Array',
372
+ 'Int16Array',
373
+ ];
374
+
375
+ const scalarLength = dimensions[0] * dimensions[1] * dimensions[2];
376
+
377
+ // Check if scalarData is provided and is of a valid type
378
+ if (!scalarData || !validDataTypes.includes(scalarData.constructor.name)) {
379
+ // Check if targetBuffer is provided and has a valid type
380
+ if (!targetBuffer?.type || !validDataTypes.includes(targetBuffer.type)) {
381
+ throw new Error(
382
+ 'createLocalVolume: parameter scalarData must be provided and must be either Uint8Array, Float32Array, Uint16Array or Int16Array'
383
+ );
384
+ }
385
+
386
+ // Generate volume scalar data if scalarData is not provided or invalid
387
+ ({ volumeScalarData: scalarData } = generateVolumeScalarData(
388
+ targetBuffer,
389
+ scalarLength
390
+ ));
392
391
  }
393
392
 
394
393
  // Todo: handle default values for spacing, origin, direction if not provided
@@ -402,8 +401,6 @@ export function createLocalVolume(
402
401
  return cachedVolume as IImageVolume;
403
402
  }
404
403
 
405
- const scalarLength = dimensions[0] * dimensions[1] * dimensions[2];
406
-
407
404
  const numBytes = scalarData ? scalarData.buffer.byteLength : scalarLength * 4;
408
405
 
409
406
  // check if there is enough space in unallocated + image Cache
@@ -436,7 +433,9 @@ export function createLocalVolume(
436
433
  imageData: imageData,
437
434
  scalarData,
438
435
  sizeInBytes: numBytes,
439
- imageIds: [],
436
+ referencedImageIds: options.referencedImageIds || [],
437
+ referencedVolumeId: options.referencedVolumeId,
438
+ imageIds: options.imageIds || [],
440
439
  });
441
440
 
442
441
  if (preventCache) {
@@ -573,7 +572,7 @@ export function getUnknownVolumeLoaderSchema(): string {
573
572
  */
574
573
  export async function createAndCacheDerivedSegmentationVolume(
575
574
  referencedVolumeId: string,
576
- options: DerivedVolumeOptions
575
+ options = {} as DerivedVolumeOptions
577
576
  ): Promise<IImageVolume> {
578
577
  return createAndCacheDerivedVolume(referencedVolumeId, {
579
578
  ...options,
@@ -582,3 +581,70 @@ export async function createAndCacheDerivedSegmentationVolume(
582
581
  },
583
582
  });
584
583
  }
584
+
585
+ /**
586
+ * Creates a local segmentation volume.
587
+ *
588
+ * @param options - The options for creating the volume.
589
+ * @param volumeId - The ID of the volume.
590
+ * @param preventCache - Whether to prevent caching the volume.
591
+ * @returns A promise that resolves to the created image volume.
592
+ */
593
+ export async function createLocalSegmentationVolume(
594
+ options: LocalVolumeOptions,
595
+ volumeId: string,
596
+ preventCache = false
597
+ ): Promise<IImageVolume> {
598
+ if (!options.scalarData) {
599
+ options.scalarData = new Uint8Array(
600
+ options.dimensions[0] * options.dimensions[1] * options.dimensions[2]
601
+ );
602
+ }
603
+
604
+ return createLocalVolume(options, volumeId, preventCache);
605
+ }
606
+
607
+ /**
608
+ * This function generates volume scalar data based on the provided target buffer and scalar length.
609
+ * It checks if the cache can accommodate the data size and throws an error if it exceeds the cache size.
610
+ * If a shared array buffer is available in the target buffer, it uses that to create the typed array.
611
+ * Otherwise, it creates a typed array based on the scalar length.
612
+ *
613
+ * @param targetBuffer - The target buffer object which may contain a type and a shared array buffer.
614
+ * @param scalarLength - The scalar length for creating the typed array.
615
+ * @param useNorm16Texture - A flag to specify whether to use a 16-bit texture or not.
616
+ * @returns The volume scalar data as a typed array.
617
+ */
618
+ function generateVolumeScalarData(
619
+ targetBuffer: {
620
+ type: PixelDataTypedArrayString;
621
+ sharedArrayBuffer?: boolean;
622
+ },
623
+ scalarLength: number
624
+ ) {
625
+ const { useNorm16Texture } = getConfiguration().rendering;
626
+
627
+ const { TypedArrayConstructor, numBytes } = getBufferConfiguration(
628
+ targetBuffer?.type,
629
+ scalarLength,
630
+ {
631
+ use16BitTexture: useNorm16Texture,
632
+ isVolumeBuffer: true,
633
+ }
634
+ );
635
+
636
+ const isCacheable = cache.isCacheable(numBytes);
637
+ if (!isCacheable) {
638
+ throw new Error(Events.CACHE_SIZE_EXCEEDED);
639
+ }
640
+
641
+ let volumeScalarData;
642
+ if (targetBuffer?.sharedArrayBuffer) {
643
+ const buffer = new SharedArrayBuffer(numBytes);
644
+ volumeScalarData = new TypedArrayConstructor(buffer);
645
+ } else {
646
+ volumeScalarData = new TypedArrayConstructor(scalarLength);
647
+ }
648
+
649
+ return { volumeScalarData, numBytes };
650
+ }
@@ -120,7 +120,13 @@ class RequestPoolManager {
120
120
  interaction: 6,
121
121
  thumbnail: 6,
122
122
  prefetch: 5,
123
- compute: 15,
123
+ // I believe there is a bug right now, where if there are two workers
124
+ // and one wants to run a compute job 6 times and the limit is just 5, then
125
+ // the other worker will never get a chance to run its compute job.
126
+ // we should probably have a separate limit for compute jobs per worker
127
+ // context as there is another layer of parallelism there. For this reason
128
+ // I'm setting the limit to 1000 for now.
129
+ compute: 1000,
124
130
  };
125
131
  }
126
132
 
@@ -1,12 +1,12 @@
1
- import { Surface } from '../cache/classes/Surface';
2
1
  import { GeometryType } from '../enums';
3
2
  import { IContourSet } from './IContourSet';
3
+ import { ISurface } from './ISurface';
4
4
 
5
5
  // interface IGeometry can be array of IContourSet
6
6
  interface IGeometry {
7
7
  id: string;
8
8
  type: GeometryType;
9
- data: IContourSet | Surface;
9
+ data: IContourSet | ISurface;
10
10
  sizeInBytes: number;
11
11
  }
12
12
 
@@ -81,10 +81,18 @@ interface IImageVolume {
81
81
  destroy(): void;
82
82
 
83
83
  /** decache */
84
- decache?: () => void;
84
+ decache?: (completelyRemove?: boolean) => void;
85
85
 
86
86
  /** */
87
87
  get imageCacheOffsetMap(): Map<string, any>;
88
+
89
+ /**
90
+ * Mark the volume as having had the pixel data changed externally
91
+ * which in background will re-configure the volume to use the new
92
+ * pixel data.
93
+ *
94
+ */
95
+ modified(): void;
88
96
  }
89
97
 
90
98
  export default IImageVolume;
@@ -0,0 +1,14 @@
1
+ import Point3 from './Point3';
2
+
3
+ export interface ISurface {
4
+ readonly id: string;
5
+ readonly sizeInBytes: number;
6
+ readonly frameOfReferenceUID: string;
7
+ getColor(): Point3;
8
+ setColor(color: Point3): void;
9
+ getPoints(): number[];
10
+ getPolys(): number[];
11
+ getSizeInBytes(): number;
12
+ setPoints(points: number[]): void;
13
+ setPolys(polys: number[]): void;
14
+ }
@@ -110,6 +110,7 @@ import type {
110
110
  InternalVideoCamera,
111
111
  VideoViewportInput,
112
112
  } from './VideoViewportTypes';
113
+ import { ISurface } from './ISurface';
113
114
  import type BoundsIJK from './BoundsIJK';
114
115
  import type { ImageVolumeProps } from './ImageVolumeProps';
115
116
  import type { VolumeProps } from './VolumeProps';
@@ -213,6 +214,7 @@ export type {
213
214
  // Surface
214
215
  PublicSurfaceData,
215
216
  SurfaceData,
217
+ ISurface,
216
218
  // Color
217
219
  RGB,
218
220
  ColormapPublic,
@@ -104,7 +104,7 @@ export default class VoxelManager<T> {
104
104
  * Records the z index modified.
105
105
  * Will record the index value if the VoxelManager is backed by a map.
106
106
  */
107
- public setAtIJKPoint = ([i, j, k], v) => this.setAtIJK(i, j, k, v);
107
+ public setAtIJKPoint = ([i, j, k]: Point3, v) => this.setAtIJK(i, j, k, v);
108
108
 
109
109
  /**
110
110
  * Gets the value at the given index.
@@ -241,6 +241,12 @@ export default class VoxelManager<T> {
241
241
  dimensions: Point3,
242
242
  scalarData
243
243
  ): VoxelManager<number> {
244
+ if (dimensions.length !== 3) {
245
+ throw new Error(
246
+ 'Dimensions must be provided as [number, number, number] for [width, height, depth]'
247
+ );
248
+ }
249
+
244
250
  const voxels = new VoxelManager(
245
251
  dimensions,
246
252
  (index) => scalarData[index],
@@ -83,6 +83,30 @@ function _processImageCacheOffsetMap(volume, scalarData) {
83
83
  * @param scalarData - The scalar data to use for the volume.
84
84
  */
85
85
  function _processVolumeImages(volume, scalarData) {
86
+ let compatibleScalarData = scalarData;
87
+
88
+ const sampleImageIdWithImage = volume.imageIds.find((imageId) => {
89
+ const image = cache.getImage(imageId);
90
+ return image;
91
+ });
92
+
93
+ if (!sampleImageIdWithImage) {
94
+ return;
95
+ }
96
+
97
+ const sampleImage = cache.getImage(sampleImageIdWithImage);
98
+ const samplePixelData =
99
+ sampleImage.imageFrame?.pixelData || sampleImage.getPixelData();
100
+
101
+ // Check if the types of scalarData and pixelData are different.
102
+ if (scalarData.constructor !== samplePixelData.constructor) {
103
+ // If so, create a new typed array of the same type as pixelData and copy the values from scalarData.
104
+ compatibleScalarData = new samplePixelData.constructor(scalarData.length);
105
+
106
+ // Copy values from scalarData to compatibleScalarData.
107
+ compatibleScalarData.set(scalarData);
108
+ }
109
+
86
110
  volume.imageIds.forEach((imageId) => {
87
111
  const image = cache.getImage(imageId);
88
112
  if (!image) {
@@ -92,7 +116,7 @@ function _processVolumeImages(volume, scalarData) {
92
116
  const index = volume.getImageIdIndex(imageId);
93
117
  const offset = index * image.getPixelData().byteLength;
94
118
 
95
- _updateImageWithScalarDataView(image, scalarData, offset);
119
+ _updateImageWithScalarDataView(image, compatibleScalarData, offset);
96
120
  cache.decrementImageCacheSize(image.sizeInBytes);
97
121
  });
98
122
  }
@@ -114,7 +114,7 @@ async function convertVolumeToStackViewport({
114
114
  imageIdIndexToJump = minDistanceIndex;
115
115
  }
116
116
 
117
- await stackViewport.setStack(stack, imageIdIndexToJump);
117
+ await stackViewport.setStack(stack, imageIdIndexToJump ?? 0);
118
118
 
119
119
  // Render the image
120
120
  stackViewport.render();
@@ -19,10 +19,9 @@ function generateVolumePropsFromImageIds(
19
19
  getConfiguration().rendering;
20
20
 
21
21
  const use16BitDataType = useNorm16Texture || preferSizeOverAccuracy;
22
-
23
22
  const volumeMetadata = makeVolumeMetadata(imageIds);
24
23
 
25
- // For a streaming volume, the data type cannot rely on cswil to load
24
+ // For a streaming volume, the data type cannot rely on CSWIL to load
26
25
  // the proper array buffer type. This is because the target buffer container
27
26
  // must be decided ahead of time.
28
27
  // TODO: move this logic into CSWIL to avoid logic duplication.
@@ -0,0 +1,22 @@
1
+ import { VolumeViewport } from '../RenderingEngine';
2
+ import cache from '../cache';
3
+ import { IViewport, IStackViewport } from '../types';
4
+
5
+ /**
6
+ * Retrieves the image IDs from the given viewport.
7
+ *
8
+ * @param viewport - The viewport to retrieve the image IDs from.
9
+ * @returns An array of image IDs.
10
+ */
11
+ function getViewportImageIds(viewport: IViewport) {
12
+ if (viewport instanceof VolumeViewport) {
13
+ const defaultActor = viewport.getDefaultActor();
14
+ const volumeId = defaultActor.uid;
15
+ const volume = cache.getVolume(volumeId);
16
+ return volume.imageIds;
17
+ } else if ((viewport as IStackViewport).getImageIds) {
18
+ return (viewport as IStackViewport).getImageIds();
19
+ }
20
+ }
21
+
22
+ export default getViewportImageIds;
@@ -68,6 +68,7 @@ import { convertVolumeToStackViewport } from './convertVolumeToStackViewport';
68
68
  import VoxelManager from './VoxelManager';
69
69
  import roundNumber, { roundToPrecision } from './roundNumber';
70
70
  import convertToGrayscale from './convertToGrayscale';
71
+ import getViewportImageIds from './getViewportImageIds';
71
72
 
72
73
  // name spaces
73
74
  import * as planar from './planar';
@@ -154,4 +155,5 @@ export {
154
155
  cacheUtils,
155
156
  roundNumber,
156
157
  roundToPrecision,
158
+ getViewportImageIds,
157
159
  };
@@ -6,11 +6,6 @@ class CentralizedWorkerManager {
6
6
  constructor() {
7
7
  this.workerRegistry = {};
8
8
  this.workerPoolManager = new RequestPoolManager('webworker');
9
- this.checkIntervalForIdleWorkers = 1000;
10
- }
11
-
12
- setCheckIntervalForIdleWorkers(value) {
13
- this.checkIntervalForIdleWorkers = value;
14
9
  }
15
10
 
16
11
  /**
@@ -29,7 +24,10 @@ class CentralizedWorkerManager {
29
24
  const {
30
25
  maxWorkerInstances = 1,
31
26
  overwrite = false,
32
- autoTerminateOnIdle = false,
27
+ autoTerminateOnIdle = {
28
+ enabled: false,
29
+ idleTimeThreshold: 3000, // 3 seconds
30
+ },
33
31
  } = options;
34
32
 
35
33
  if (this.workerRegistry[workerName] && !overwrite) {
@@ -43,25 +41,17 @@ class CentralizedWorkerManager {
43
41
 
44
42
  const workerProperties = {
45
43
  workerFn: null,
46
- idleCheckIntervalId: null,
47
44
  instances: [],
48
45
  loadCounters: [],
49
46
  lastActiveTime: [],
50
47
  // used for termination
51
48
  nativeWorkers: [],
49
+ // auto termination
50
+ autoTerminateOnIdle: autoTerminateOnIdle.enabled,
51
+ idleCheckIntervalId: null,
52
+ idleTimeThreshold: autoTerminateOnIdle.idleTimeThreshold,
52
53
  };
53
54
 
54
- if (
55
- (autoTerminateOnIdle && !workerProperties.idleCheckIntervalId) ||
56
- overwrite
57
- ) {
58
- const idleCheckIntervalId = setInterval(() => {
59
- this.terminateIdleWorkers(workerName, autoTerminateOnIdle);
60
- }, this.checkIntervalForIdleWorkers);
61
-
62
- workerProperties.idleCheckIntervalId = idleCheckIntervalId;
63
- }
64
-
65
55
  workerProperties.loadCounters = Array(maxWorkerInstances).fill(0);
66
56
  workerProperties.lastActiveTime = Array(maxWorkerInstances).fill(null);
67
57
 
@@ -90,7 +80,6 @@ class CentralizedWorkerManager {
90
80
 
91
81
  let minLoadIndex = 0;
92
82
  let minLoadValue = workerProperties.loadCounters[0] || 0;
93
-
94
83
  for (let i = 1; i < workerInstances.length; i++) {
95
84
  const currentLoadValue = workerProperties.loadCounters[i] || 0;
96
85
  if (currentLoadValue < minLoadValue) {
@@ -121,7 +110,10 @@ class CentralizedWorkerManager {
121
110
  *
122
111
  * @param workerName - The name of the worker to execute the task on.
123
112
  * @param methodName - The name of the method to execute on the worker.
124
- * @param args - The arguments to pass to the method. Default is an empty object.
113
+ * @param args - The arguments to pass to the method. Default is an array
114
+ * You should put your transferable objects in the first argument as object
115
+ * and from the second argument you can put your non-transferable objects such
116
+ * as functions, classes, etc.
125
117
  * @param options - An object containing options for the request. Default is an empty object.
126
118
  * @param options.requestType - The type of the request. Default is RequestType.Compute.
127
119
  * @param options.priority - The priority of the request. Default is 0.
@@ -133,7 +125,12 @@ class CentralizedWorkerManager {
133
125
  workerName,
134
126
  methodName,
135
127
  args = {},
136
- { requestType = RequestType.Compute, priority = 0, options = {} } = {}
128
+ {
129
+ requestType = RequestType.Compute,
130
+ priority = 0,
131
+ options = {},
132
+ callbacks = [],
133
+ } = {}
137
134
  ) {
138
135
  return new Promise((resolve, reject) => {
139
136
  const requestFn = async () => {
@@ -148,11 +145,34 @@ class CentralizedWorkerManager {
148
145
  }
149
146
 
150
147
  try {
151
- const results = await api[methodName](args);
152
-
148
+ // fix if any of the args keys are a function then we need to proxy it
149
+ // for the worker to be able to call it
150
+ let finalCallbacks;
151
+ if (callbacks.length) {
152
+ finalCallbacks = callbacks.map((cb) => {
153
+ return Comlink.proxy(cb);
154
+ });
155
+ }
153
156
  const workerProperties = this.workerRegistry[workerName];
157
+
158
+ const results = await api[methodName](args, ...finalCallbacks);
159
+
154
160
  workerProperties.lastActiveTime[index] = Date.now();
155
161
 
162
+ // If auto termination is enabled and the interval is not set, set it.
163
+ if (
164
+ workerProperties.autoTerminateOnIdle &&
165
+ !workerProperties.idleCheckIntervalId &&
166
+ workerProperties.idleTimeThreshold
167
+ ) {
168
+ workerProperties.idleCheckIntervalId = setInterval(() => {
169
+ this.terminateIdleWorkers(
170
+ workerName,
171
+ workerProperties.idleTimeThreshold
172
+ );
173
+ }, workerProperties.idleTimeThreshold);
174
+ }
175
+
156
176
  resolve(results);
157
177
  } catch (err) {
158
178
  console.error(
@@ -165,6 +185,11 @@ class CentralizedWorkerManager {
165
185
  }
166
186
  };
167
187
 
188
+ // I believe there is a bug right now, where if there are two workers
189
+ // and one wants to run a compute job 6 times and the limit is just 5, then
190
+ // the other worker will never get a chance to run its compute job.
191
+ // we should probably have a separate limit for compute jobs per worker
192
+ // context as there is another layer of parallelism there.
168
193
  this.workerPoolManager.addRequest(
169
194
  requestFn,
170
195
  requestType,
@@ -176,24 +201,16 @@ class CentralizedWorkerManager {
176
201
 
177
202
  terminateIdleWorkers(workerName, idleTimeThreshold) {
178
203
  const workerProperties = this.workerRegistry[workerName];
179
-
180
204
  const now = Date.now();
181
205
 
182
- workerProperties.instances.forEach((workerInstance, index) => {
183
- // If the worker has not yet executed any task, skip this iteration
184
- if (workerProperties.lastActiveTime[index] == null) {
185
- return;
186
- }
187
-
188
- const idleTime = now - workerProperties.lastActiveTime[index];
206
+ workerProperties.instances.forEach((_, index) => {
207
+ const lastActiveTime = workerProperties.lastActiveTime[index];
208
+ const isWorkerActive =
209
+ lastActiveTime !== null && workerProperties.loadCounters[index] > 0;
210
+ const idleTime = now - lastActiveTime;
189
211
 
190
- // If the worker has been idle for longer than the threshold and it exists
191
- if (idleTime > idleTimeThreshold && workerInstance !== null) {
192
- workerInstance[Comlink.releaseProxy]();
193
- workerProperties.nativeWorkers[index].terminate();
194
-
195
- workerProperties.instances[index] = null;
196
- workerProperties.lastActiveTime[index] = null;
212
+ if (!isWorkerActive && idleTime > idleTimeThreshold) {
213
+ this.terminateWorkerInstance(workerName, index);
197
214
  }
198
215
  });
199
216
  }
@@ -205,13 +222,24 @@ class CentralizedWorkerManager {
205
222
  return;
206
223
  }
207
224
 
208
- workerProperties.instances.forEach((workerInstance) => {
209
- workerInstance[Comlink.releaseProxy]();
225
+ workerProperties.instances.forEach((_, index) => {
226
+ this.terminateWorkerInstance(workerName, index);
210
227
  });
228
+ }
211
229
 
212
- workerProperties.nativeWorkers.forEach((worker) => {
213
- worker.terminate();
214
- });
230
+ // New method to handle individual worker termination
231
+ terminateWorkerInstance(workerName, index) {
232
+ const workerProperties = this.workerRegistry[workerName];
233
+ const workerInstance = workerProperties.instances[index];
234
+
235
+ if (workerInstance !== null) {
236
+ workerInstance[Comlink.releaseProxy]();
237
+ workerProperties.nativeWorkers[index].terminate();
238
+
239
+ // Set the worker instance to null after termination
240
+ workerProperties.instances[index] = null;
241
+ workerProperties.lastActiveTime[index] = null;
242
+ }
215
243
  }
216
244
  }
217
245