@cornerstonejs/core 1.41.0 → 1.42.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 (162) hide show
  1. package/dist/cjs/RenderingEngine/BaseVolumeViewport.js.map +1 -1
  2. package/dist/cjs/RenderingEngine/StackViewport.js +2 -1
  3. package/dist/cjs/RenderingEngine/StackViewport.js.map +1 -1
  4. package/dist/cjs/RenderingEngine/helpers/createVolumeActor.js +2 -1
  5. package/dist/cjs/RenderingEngine/helpers/createVolumeActor.js.map +1 -1
  6. package/dist/cjs/cache/cache.d.ts +6 -3
  7. package/dist/cjs/cache/cache.js +43 -6
  8. package/dist/cjs/cache/cache.js.map +1 -1
  9. package/dist/cjs/cache/classes/ImageVolume.d.ts +25 -5
  10. package/dist/cjs/cache/classes/ImageVolume.js +302 -13
  11. package/dist/cjs/cache/classes/ImageVolume.js.map +1 -1
  12. package/dist/cjs/eventTarget.d.ts +1 -0
  13. package/dist/cjs/eventTarget.js +13 -1
  14. package/dist/cjs/eventTarget.js.map +1 -1
  15. package/dist/cjs/init.js +2 -0
  16. package/dist/cjs/init.js.map +1 -1
  17. package/dist/cjs/loaders/imageLoader.js +6 -2
  18. package/dist/cjs/loaders/imageLoader.js.map +1 -1
  19. package/dist/cjs/loaders/volumeLoader.d.ts +12 -9
  20. package/dist/cjs/loaders/volumeLoader.js +50 -5
  21. package/dist/cjs/loaders/volumeLoader.js.map +1 -1
  22. package/dist/cjs/types/Cornerstone3DConfig.d.ts +1 -0
  23. package/dist/cjs/types/IDynamicImageVolume.d.ts +2 -2
  24. package/dist/cjs/types/IImage.d.ts +5 -0
  25. package/dist/cjs/types/IImageVolume.d.ts +7 -2
  26. package/dist/cjs/types/ILoadObject.d.ts +2 -2
  27. package/dist/cjs/types/IVolume.d.ts +3 -26
  28. package/dist/cjs/types/ImageVolumeProps.d.ts +6 -0
  29. package/dist/cjs/types/ImageVolumeProps.js +3 -0
  30. package/dist/cjs/types/ImageVolumeProps.js.map +1 -0
  31. package/dist/cjs/types/VolumeProps.d.ts +27 -0
  32. package/dist/cjs/types/VolumeProps.js +3 -0
  33. package/dist/cjs/types/VolumeProps.js.map +1 -0
  34. package/dist/cjs/types/index.d.ts +4 -2
  35. package/dist/cjs/utilities/VoxelManager.d.ts +2 -2
  36. package/dist/cjs/utilities/cacheUtils.d.ts +2 -0
  37. package/dist/cjs/utilities/cacheUtils.js +96 -0
  38. package/dist/cjs/utilities/cacheUtils.js.map +1 -0
  39. package/dist/cjs/utilities/convertStackToVolumeViewport.d.ts +12 -0
  40. package/dist/cjs/utilities/convertStackToVolumeViewport.js +61 -0
  41. package/dist/cjs/utilities/convertStackToVolumeViewport.js.map +1 -0
  42. package/dist/cjs/utilities/convertVolumeToStackViewport.d.ts +9 -0
  43. package/dist/cjs/utilities/convertVolumeToStackViewport.js +95 -0
  44. package/dist/cjs/utilities/convertVolumeToStackViewport.js.map +1 -0
  45. package/dist/cjs/utilities/createSigmoidRGBTransferFunction.js +25 -2
  46. package/dist/cjs/utilities/createSigmoidRGBTransferFunction.js.map +1 -1
  47. package/dist/cjs/utilities/generateVolumePropsFromImageIds.d.ts +3 -0
  48. package/dist/cjs/utilities/generateVolumePropsFromImageIds.js +124 -0
  49. package/dist/cjs/utilities/generateVolumePropsFromImageIds.js.map +1 -0
  50. package/dist/cjs/utilities/index.d.ts +6 -1
  51. package/dist/cjs/utilities/index.js +12 -1
  52. package/dist/cjs/utilities/index.js.map +1 -1
  53. package/dist/cjs/utilities/roundNumber.d.ts +4 -0
  54. package/dist/cjs/utilities/roundNumber.js +36 -0
  55. package/dist/cjs/utilities/roundNumber.js.map +1 -0
  56. package/dist/esm/RenderingEngine/BaseVolumeViewport.js.map +1 -1
  57. package/dist/esm/RenderingEngine/StackViewport.js +2 -1
  58. package/dist/esm/RenderingEngine/StackViewport.js.map +1 -1
  59. package/dist/esm/RenderingEngine/helpers/createVolumeActor.js +1 -1
  60. package/dist/esm/RenderingEngine/helpers/createVolumeActor.js.map +1 -1
  61. package/dist/esm/cache/cache.js +43 -6
  62. package/dist/esm/cache/cache.js.map +1 -1
  63. package/dist/esm/cache/classes/ImageVolume.js +279 -14
  64. package/dist/esm/cache/classes/ImageVolume.js.map +1 -1
  65. package/dist/esm/eventTarget.js +13 -1
  66. package/dist/esm/eventTarget.js.map +1 -1
  67. package/dist/esm/init.js +2 -0
  68. package/dist/esm/init.js.map +1 -1
  69. package/dist/esm/loaders/imageLoader.js +5 -2
  70. package/dist/esm/loaders/imageLoader.js.map +1 -1
  71. package/dist/esm/loaders/volumeLoader.js +50 -5
  72. package/dist/esm/loaders/volumeLoader.js.map +1 -1
  73. package/dist/esm/types/ImageVolumeProps.js +2 -0
  74. package/dist/esm/types/ImageVolumeProps.js.map +1 -0
  75. package/dist/esm/types/VolumeProps.js +2 -0
  76. package/dist/esm/types/VolumeProps.js.map +1 -0
  77. package/dist/esm/utilities/cacheUtils.js +65 -0
  78. package/dist/esm/utilities/cacheUtils.js.map +1 -0
  79. package/dist/esm/utilities/convertStackToVolumeViewport.js +49 -0
  80. package/dist/esm/utilities/convertStackToVolumeViewport.js.map +1 -0
  81. package/dist/esm/utilities/convertVolumeToStackViewport.js +58 -0
  82. package/dist/esm/utilities/convertVolumeToStackViewport.js.map +1 -0
  83. package/dist/esm/utilities/createSigmoidRGBTransferFunction.js +1 -1
  84. package/dist/esm/utilities/createSigmoidRGBTransferFunction.js.map +1 -1
  85. package/dist/esm/utilities/generateVolumePropsFromImageIds.js +118 -0
  86. package/dist/esm/utilities/generateVolumePropsFromImageIds.js.map +1 -0
  87. package/dist/esm/utilities/index.js +6 -1
  88. package/dist/esm/utilities/index.js.map +1 -1
  89. package/dist/esm/utilities/roundNumber.js +33 -0
  90. package/dist/esm/utilities/roundNumber.js.map +1 -0
  91. package/dist/types/RenderingEngine/BaseVolumeViewport.d.ts.map +1 -1
  92. package/dist/types/RenderingEngine/StackViewport.d.ts.map +1 -1
  93. package/dist/types/cache/cache.d.ts +6 -3
  94. package/dist/types/cache/cache.d.ts.map +1 -1
  95. package/dist/types/cache/classes/ImageVolume.d.ts +25 -5
  96. package/dist/types/cache/classes/ImageVolume.d.ts.map +1 -1
  97. package/dist/types/eventTarget.d.ts +1 -0
  98. package/dist/types/eventTarget.d.ts.map +1 -1
  99. package/dist/types/loaders/imageLoader.d.ts.map +1 -1
  100. package/dist/types/loaders/volumeLoader.d.ts +12 -9
  101. package/dist/types/loaders/volumeLoader.d.ts.map +1 -1
  102. package/dist/types/types/Cornerstone3DConfig.d.ts +1 -0
  103. package/dist/types/types/Cornerstone3DConfig.d.ts.map +1 -1
  104. package/dist/types/types/IDynamicImageVolume.d.ts +2 -2
  105. package/dist/types/types/IDynamicImageVolume.d.ts.map +1 -1
  106. package/dist/types/types/IImage.d.ts +5 -0
  107. package/dist/types/types/IImage.d.ts.map +1 -1
  108. package/dist/types/types/IImageVolume.d.ts +7 -2
  109. package/dist/types/types/IImageVolume.d.ts.map +1 -1
  110. package/dist/types/types/ILoadObject.d.ts +2 -2
  111. package/dist/types/types/ILoadObject.d.ts.map +1 -1
  112. package/dist/types/types/IVolume.d.ts +3 -26
  113. package/dist/types/types/IVolume.d.ts.map +1 -1
  114. package/dist/types/types/ImageVolumeProps.d.ts +7 -0
  115. package/dist/types/types/ImageVolumeProps.d.ts.map +1 -0
  116. package/dist/types/types/VolumeProps.d.ts +28 -0
  117. package/dist/types/types/VolumeProps.d.ts.map +1 -0
  118. package/dist/types/types/index.d.ts +4 -2
  119. package/dist/types/types/index.d.ts.map +1 -1
  120. package/dist/types/utilities/VoxelManager.d.ts +2 -2
  121. package/dist/types/utilities/VoxelManager.d.ts.map +1 -1
  122. package/dist/types/utilities/cacheUtils.d.ts +3 -0
  123. package/dist/types/utilities/cacheUtils.d.ts.map +1 -0
  124. package/dist/types/utilities/convertStackToVolumeViewport.d.ts +13 -0
  125. package/dist/types/utilities/convertStackToVolumeViewport.d.ts.map +1 -0
  126. package/dist/types/utilities/convertVolumeToStackViewport.d.ts +10 -0
  127. package/dist/types/utilities/convertVolumeToStackViewport.d.ts.map +1 -0
  128. package/dist/types/utilities/generateVolumePropsFromImageIds.d.ts +4 -0
  129. package/dist/types/utilities/generateVolumePropsFromImageIds.d.ts.map +1 -0
  130. package/dist/types/utilities/index.d.ts +6 -1
  131. package/dist/types/utilities/index.d.ts.map +1 -1
  132. package/dist/types/utilities/roundNumber.d.ts +5 -0
  133. package/dist/types/utilities/roundNumber.d.ts.map +1 -0
  134. package/dist/umd/index.js +1 -1
  135. package/dist/umd/index.js.map +1 -1
  136. package/package.json +2 -2
  137. package/src/RenderingEngine/BaseVolumeViewport.ts +0 -1
  138. package/src/RenderingEngine/StackViewport.ts +3 -1
  139. package/src/RenderingEngine/helpers/createVolumeActor.ts +1 -1
  140. package/src/cache/cache.ts +91 -7
  141. package/src/cache/classes/ImageVolume.ts +535 -21
  142. package/src/eventTarget.ts +19 -1
  143. package/src/init.ts +2 -2
  144. package/src/loaders/imageLoader.ts +6 -2
  145. package/src/loaders/volumeLoader.ts +118 -23
  146. package/src/types/Cornerstone3DConfig.ts +12 -0
  147. package/src/types/IDynamicImageVolume.ts +2 -2
  148. package/src/types/IImage.ts +6 -0
  149. package/src/types/IImageVolume.ts +14 -2
  150. package/src/types/ILoadObject.ts +2 -2
  151. package/src/types/IVolume.ts +4 -41
  152. package/src/types/ImageVolumeProps.ts +15 -0
  153. package/src/types/VolumeProps.ts +57 -0
  154. package/src/types/index.ts +5 -2
  155. package/src/utilities/VoxelManager.ts +2 -2
  156. package/src/utilities/cacheUtils.ts +121 -0
  157. package/src/utilities/convertStackToVolumeViewport.ts +115 -0
  158. package/src/utilities/convertVolumeToStackViewport.ts +125 -0
  159. package/src/utilities/createSigmoidRGBTransferFunction.ts +1 -1
  160. package/src/utilities/generateVolumePropsFromImageIds.ts +183 -0
  161. package/src/utilities/index.ts +11 -0
  162. package/src/utilities/roundNumber.ts +56 -0
@@ -0,0 +1,115 @@
1
+ import { IStackViewport, IVolumeViewport, Point3 } from '../types';
2
+ import { setVolumesForViewports } from '../RenderingEngine/helpers';
3
+ import {
4
+ createAndCacheVolume,
5
+ getUnknownVolumeLoaderSchema,
6
+ } from '../loaders/volumeLoader';
7
+ import { Events, OrientationAxis, ViewportType } from '../enums';
8
+
9
+ /**
10
+ * Converts a stack viewport to a volume viewport.
11
+ *
12
+ * @param params - The parameters for the conversion.
13
+ * @param params.viewport - The stack viewport to convert.
14
+ * @param params.options - The options for the conversion.
15
+ * @param [params.options.volumeId] - The volumeId that will get generated, it should have the volume loader schema defined if not we will use the default one.
16
+ * @param [params.options.viewportId] - The viewportId that will get used for new viewport. If not provided, the stack viewport id will be used.
17
+ * @param [params.options.background] - The background color of the volume viewport.
18
+ * @returns The converted volume viewport.
19
+ */
20
+ async function convertStackToVolumeViewport({
21
+ viewport,
22
+ options,
23
+ }: {
24
+ viewport: IStackViewport;
25
+ options: {
26
+ volumeId: string;
27
+ viewportId?: string;
28
+ background?: Point3;
29
+ orientation?: OrientationAxis;
30
+ };
31
+ }): Promise<IVolumeViewport> {
32
+ const renderingEngine = viewport.getRenderingEngine();
33
+
34
+ let { volumeId } = options;
35
+
36
+ // if there is no loader schema for the volume Id, we will use the default one
37
+ // which we can get from the volume loader
38
+ if (volumeId.split(':').length === 1) {
39
+ const schema = getUnknownVolumeLoaderSchema();
40
+ volumeId = `${schema}:${volumeId}`;
41
+ }
42
+
43
+ const { id, element } = viewport;
44
+ const viewportId = options.viewportId || id;
45
+
46
+ const imageIds = viewport.getImageIds();
47
+
48
+ // It is important to keep the camera before enabling the viewport
49
+ const prevCamera = viewport.getCamera();
50
+
51
+ // this will disable the stack viewport and remove it from the toolGroup
52
+ renderingEngine.enableElement({
53
+ viewportId,
54
+ type: ViewportType.ORTHOGRAPHIC,
55
+ element,
56
+ defaultOptions: {
57
+ background: options.background,
58
+ orientation: options.orientation,
59
+ },
60
+ });
61
+
62
+ // Ideally here we should be able to just create a local volume and not use the
63
+ // volume louder but we don't know if the stack is already pre-cached for all its
64
+ // imageIds or not so we just let the loader handle it and we have cache
65
+ // optimizations in place to avoid fetching the same imageId if it is already
66
+ // cached
67
+ const volume = await createAndCacheVolume(volumeId, {
68
+ imageIds,
69
+ });
70
+
71
+ volume.load();
72
+
73
+ // we should get the new viewport from the renderingEngine since the stack viewport
74
+ // was disabled and replaced with a volume viewport of the same id
75
+ const volumeViewport = <IVolumeViewport>(
76
+ renderingEngine.getViewport(viewportId)
77
+ );
78
+
79
+ setVolumesForViewports(
80
+ renderingEngine,
81
+ [
82
+ {
83
+ volumeId,
84
+ },
85
+ ],
86
+ [viewportId]
87
+ );
88
+
89
+ const volumeViewportNewVolumeHandler = () => {
90
+ volumeViewport.setCamera({
91
+ ...prevCamera,
92
+ });
93
+ volumeViewport.render();
94
+
95
+ element.removeEventListener(
96
+ Events.VOLUME_VIEWPORT_NEW_VOLUME,
97
+ volumeViewportNewVolumeHandler
98
+ );
99
+ };
100
+
101
+ const addVolumeViewportNewVolumeListener = () => {
102
+ element.addEventListener(
103
+ Events.VOLUME_VIEWPORT_NEW_VOLUME,
104
+ volumeViewportNewVolumeHandler
105
+ );
106
+ };
107
+
108
+ addVolumeViewportNewVolumeListener();
109
+
110
+ volumeViewport.render();
111
+
112
+ return volumeViewport;
113
+ }
114
+
115
+ export { convertStackToVolumeViewport };
@@ -0,0 +1,125 @@
1
+ import * as Types from '../types';
2
+ import cache, { ImageVolume } from '../cache';
3
+ import { ViewportType } from '../enums';
4
+
5
+ /**
6
+ * Converts a volume viewport to a stack viewport.
7
+ *
8
+ * @param params - The parameters for the conversion.
9
+ * @param params.viewport - The volume viewport to convert.
10
+ * @param params.options - The conversion options.
11
+ * @param [params.options.viewportId] - The new stackViewportId, If not provided, the volume viewport id will be used.
12
+ * @param [params.options.background] - The background color of the stack viewport.
13
+ * @param [params.options.decache] - Whether to decache the volume. Defaults to false.
14
+ *
15
+ * @returns The converted stack viewport.
16
+ */
17
+ async function convertVolumeToStackViewport({
18
+ viewport,
19
+ options,
20
+ }: {
21
+ viewport: Types.IVolumeViewport;
22
+ options: {
23
+ viewportId?: string;
24
+ background?: Types.Point3;
25
+ };
26
+ }): Promise<Types.IStackViewport> {
27
+ const volumeViewport = viewport;
28
+ const { id, element } = volumeViewport;
29
+ const renderingEngine = viewport.getRenderingEngine();
30
+ const imageIdIndex = viewport.getCurrentImageIdIndex();
31
+
32
+ const { background } = options;
33
+ const viewportId = options.viewportId || id;
34
+
35
+ const actorEntry = volumeViewport.getDefaultActor();
36
+ const { uid: volumeId } = actorEntry;
37
+ const volume = cache.getVolume(volumeId) as Types.IImageVolume;
38
+
39
+ if (!(volume instanceof ImageVolume)) {
40
+ throw new Error(
41
+ 'Currently, you cannot decache a volume that is not an ImageVolume. So, unfortunately, volumes such as nifti (which are basic Volume, without imageIds) cannot be decached.'
42
+ );
43
+ }
44
+
45
+ const viewportInput = {
46
+ viewportId,
47
+ type: ViewportType.STACK,
48
+ element,
49
+ defaultOptions: {
50
+ background,
51
+ },
52
+ };
53
+
54
+ renderingEngine.enableElement(viewportInput);
55
+
56
+ // Get the stack viewport that was created
57
+ const stackViewport = <Types.IStackViewport>(
58
+ renderingEngine.getViewport(viewportId)
59
+ );
60
+
61
+ // So here we have two scenarios that we need to handle:
62
+ // 1. the volume was derived from a stack and we need to decache it, this is easy
63
+ // since we just need purge the volume from the cache and those images will get
64
+ // their copy of the image back
65
+ // 2. It was actually a native volume and we need to decache it, this is a bit more
66
+ // complicated since then we need to decide on the imageIds for it to get
67
+ // decached to
68
+ const hasCachedImages = volume.imageCacheOffsetMap.size > 0;
69
+ // Initialize the variable to hold the final result
70
+ let isAllImagesCached = false;
71
+
72
+ if (hasCachedImages) {
73
+ // Check if every imageId in the volume is in the _imageCache
74
+ isAllImagesCached = volume.imageIds.every((imageId) =>
75
+ cache.getImage(imageId)
76
+ );
77
+ }
78
+
79
+ const volumeUsedInOtherViewports = renderingEngine
80
+ .getVolumeViewports()
81
+ .find((vp) => vp.hasVolumeId(volumeId));
82
+
83
+ volume.decache(!volumeUsedInOtherViewports && isAllImagesCached);
84
+
85
+ const stack = [...volume.imageIds].reverse();
86
+
87
+ let imageIdIndexToJump = Math.max(
88
+ volume.imageIds.length - imageIdIndex - 1,
89
+ 0
90
+ );
91
+
92
+ // Check to see if the image is already cached or not. If it's not, we will use another
93
+ // nearby imageId for the first image to jump to. There seem to be a lot of side effects
94
+ // if we jump to an image that is not cached in stack viewport while we convert
95
+ // from a volume viewport. For example, if we switch back and forth between stack and volume,
96
+ // and then try to jump to an image that is not cached, the image will not render at
97
+ // all when the full volume is filled. I'm not sure why yet.
98
+ const imageToJump = cache.getImage(stack[imageIdIndexToJump]);
99
+ if (!imageToJump) {
100
+ let minDistance = Infinity;
101
+ let minDistanceIndex = null;
102
+
103
+ stack.forEach((imageId, index) => {
104
+ const image = cache.getImage(imageId);
105
+ if (image) {
106
+ const distance = Math.abs(imageIdIndexToJump - index);
107
+ if (distance < minDistance) {
108
+ minDistance = distance;
109
+ minDistanceIndex = index;
110
+ }
111
+ }
112
+ });
113
+
114
+ imageIdIndexToJump = minDistanceIndex;
115
+ }
116
+
117
+ await stackViewport.setStack(stack, imageIdIndexToJump);
118
+
119
+ // Render the image
120
+ stackViewport.render();
121
+
122
+ return stackViewport;
123
+ }
124
+
125
+ export { convertVolumeToStackViewport };
@@ -1,7 +1,7 @@
1
1
  import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction';
2
2
  import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray';
3
3
  import { VOIRange } from '../types';
4
- import { windowLevel as windowLevelUtil } from '.';
4
+ import * as windowLevelUtil from './windowLevel';
5
5
 
6
6
  /**
7
7
  * A utility that can be used to generate an Sigmoid RgbTransferFunction.
@@ -0,0 +1,183 @@
1
+ import { vec3 } from 'gl-matrix';
2
+ import { getConfiguration, getShouldUseSharedArrayBuffer } from '../init';
3
+ import createFloat32SharedArray from './createFloat32SharedArray';
4
+ import createInt16SharedArray from './createInt16SharedArray';
5
+ import createUint16SharedArray from './createUInt16SharedArray';
6
+ import createUint8SharedArray from './createUint8SharedArray';
7
+ import getScalingParameters from './getScalingParameters';
8
+ import makeVolumeMetadata from './makeVolumeMetadata';
9
+ import sortImageIdsAndGetSpacing from './sortImageIdsAndGetSpacing';
10
+ import { ImageVolumeProps, Mat3, Point3 } from '../types';
11
+ import cache from '../cache';
12
+ import { Events } from '../enums';
13
+
14
+ function generateVolumePropsFromImageIds(
15
+ imageIds: string[],
16
+ volumeId: string
17
+ ): ImageVolumeProps {
18
+ const { useNorm16Texture, preferSizeOverAccuracy } =
19
+ getConfiguration().rendering;
20
+
21
+ const use16BitDataType = useNorm16Texture || preferSizeOverAccuracy;
22
+
23
+ const volumeMetadata = makeVolumeMetadata(imageIds);
24
+
25
+ // For a streaming volume, the data type cannot rely on cswil to load
26
+ // the proper array buffer type. This is because the target buffer container
27
+ // must be decided ahead of time.
28
+ // TODO: move this logic into CSWIL to avoid logic duplication.
29
+ // We check if scaling parameters are negative we choose Int16 instead of
30
+ // Uint16 for cases where BitsAllocated is 16.
31
+ const imageIdIndex = Math.floor(imageIds.length / 2);
32
+ const imageId = imageIds[imageIdIndex];
33
+ const scalingParameters = getScalingParameters(imageId);
34
+ const hasNegativeRescale =
35
+ scalingParameters.rescaleIntercept < 0 ||
36
+ scalingParameters.rescaleSlope < 0;
37
+
38
+ // The prescale is ALWAYS used with modality LUT, so we can assume that
39
+ // if the rescale slope is not an integer, we need to use Float32
40
+ const hasFloatRescale =
41
+ scalingParameters.rescaleIntercept % 1 !== 0 ||
42
+ scalingParameters.rescaleSlope % 1 !== 0;
43
+
44
+ const {
45
+ BitsAllocated,
46
+ PixelRepresentation,
47
+ PhotometricInterpretation,
48
+ ImageOrientationPatient,
49
+ PixelSpacing,
50
+ Columns,
51
+ Rows,
52
+ } = volumeMetadata;
53
+
54
+ const rowCosineVec = vec3.fromValues(
55
+ ImageOrientationPatient[0],
56
+ ImageOrientationPatient[1],
57
+ ImageOrientationPatient[2]
58
+ );
59
+ const colCosineVec = vec3.fromValues(
60
+ ImageOrientationPatient[3],
61
+ ImageOrientationPatient[4],
62
+ ImageOrientationPatient[5]
63
+ );
64
+
65
+ const scanAxisNormal = vec3.create();
66
+
67
+ vec3.cross(scanAxisNormal, rowCosineVec, colCosineVec);
68
+
69
+ const { zSpacing, origin, sortedImageIds } = sortImageIdsAndGetSpacing(
70
+ imageIds,
71
+ scanAxisNormal
72
+ );
73
+
74
+ const numFrames = imageIds.length;
75
+
76
+ // Spacing goes [1] then [0], as [1] is column spacing (x) and [0] is row spacing (y)
77
+ const spacing = <Point3>[PixelSpacing[1], PixelSpacing[0], zSpacing];
78
+ const dimensions = <Point3>[Columns, Rows, numFrames];
79
+ const direction = [
80
+ ...rowCosineVec,
81
+ ...colCosineVec,
82
+ ...scanAxisNormal,
83
+ ] as Mat3;
84
+ const signed = PixelRepresentation === 1;
85
+ const numComponents = PhotometricInterpretation === 'RGB' ? 3 : 1;
86
+ const useSharedArrayBuffer = getShouldUseSharedArrayBuffer();
87
+ const length = dimensions[0] * dimensions[1] * dimensions[2];
88
+ const handleCache = (sizeInBytes) => {
89
+ if (!cache.isCacheable(sizeInBytes)) {
90
+ throw new Error(Events.CACHE_SIZE_EXCEEDED);
91
+ }
92
+ cache.decacheIfNecessaryUntilBytesAvailable(sizeInBytes);
93
+ };
94
+
95
+ let scalarData, sizeInBytes;
96
+ switch (BitsAllocated) {
97
+ case 8:
98
+ if (signed) {
99
+ throw new Error(
100
+ '8 Bit signed images are not yet supported by this plugin.'
101
+ );
102
+ }
103
+ sizeInBytes = length * numComponents;
104
+ handleCache(sizeInBytes);
105
+ scalarData = useSharedArrayBuffer
106
+ ? createUint8SharedArray(length * numComponents)
107
+ : new Uint8Array(length * numComponents);
108
+ break;
109
+
110
+ case 16:
111
+ // Temporary fix for 16 bit images to use Float32
112
+ // until the new dicom image loader handler the conversion
113
+ // correctly
114
+ if (!use16BitDataType || hasFloatRescale) {
115
+ sizeInBytes = length * 4;
116
+ scalarData = useSharedArrayBuffer
117
+ ? createFloat32SharedArray(length)
118
+ : new Float32Array(length);
119
+
120
+ break;
121
+ }
122
+
123
+ sizeInBytes = length * 2;
124
+ if (signed || hasNegativeRescale) {
125
+ handleCache(sizeInBytes);
126
+ scalarData = useSharedArrayBuffer
127
+ ? createInt16SharedArray(length)
128
+ : new Int16Array(length);
129
+ break;
130
+ }
131
+
132
+ if (!signed && !hasNegativeRescale) {
133
+ handleCache(sizeInBytes);
134
+ scalarData = useSharedArrayBuffer
135
+ ? createUint16SharedArray(length)
136
+ : new Uint16Array(length);
137
+ break;
138
+ }
139
+
140
+ // Default to Float32 again
141
+ sizeInBytes = length * 4;
142
+ handleCache(sizeInBytes);
143
+ scalarData = useSharedArrayBuffer
144
+ ? createFloat32SharedArray(length)
145
+ : new Float32Array(length);
146
+ break;
147
+
148
+ case 24:
149
+ sizeInBytes = length * numComponents;
150
+ handleCache(sizeInBytes);
151
+
152
+ // hacky because we don't support alpha channel in dicom
153
+ scalarData = useSharedArrayBuffer
154
+ ? createUint8SharedArray(length * numComponents)
155
+ : new Uint8Array(length * numComponents);
156
+ break;
157
+ case 32:
158
+ sizeInBytes = length * 4;
159
+ handleCache(sizeInBytes);
160
+ scalarData = useSharedArrayBuffer
161
+ ? createFloat32SharedArray(length)
162
+ : new Float32Array(length);
163
+ break;
164
+ default:
165
+ throw new Error(
166
+ `Bits allocated of ${BitsAllocated} is not defined to generate scalarData for the volume.`
167
+ );
168
+ }
169
+
170
+ return {
171
+ dimensions,
172
+ spacing,
173
+ origin,
174
+ direction,
175
+ scalarData,
176
+ sizeInBytes,
177
+ metadata: volumeMetadata,
178
+ imageIds: sortedImageIds,
179
+ volumeId,
180
+ };
181
+ }
182
+
183
+ export { generateVolumePropsFromImageIds };
@@ -61,13 +61,18 @@ import decimate from './decimate';
61
61
  import imageRetrieveMetadataProvider from './imageRetrieveMetadataProvider';
62
62
  import isVideoTransferSyntax from './isVideoTransferSyntax';
63
63
  import { getBufferConfiguration } from './getBufferConfiguration';
64
+ import { generateVolumePropsFromImageIds } from './generateVolumePropsFromImageIds';
65
+ import { convertStackToVolumeViewport } from './convertStackToVolumeViewport';
66
+ import { convertVolumeToStackViewport } from './convertVolumeToStackViewport';
64
67
  import VoxelManager from './VoxelManager';
68
+ import roundNumber, { roundToPrecision } from './roundNumber';
65
69
 
66
70
  // name spaces
67
71
  import * as planar from './planar';
68
72
  import * as windowLevel from './windowLevel';
69
73
  import * as colormap from './colormap';
70
74
  import * as transferFunctionUtils from './transferFunctionUtils';
75
+ import * as cacheUtils from './cacheUtils';
71
76
 
72
77
  export {
73
78
  eventListener,
@@ -139,4 +144,10 @@ export {
139
144
  isVideoTransferSyntax,
140
145
  getBufferConfiguration,
141
146
  VoxelManager,
147
+ generateVolumePropsFromImageIds,
148
+ convertStackToVolumeViewport,
149
+ convertVolumeToStackViewport,
150
+ cacheUtils,
151
+ roundNumber,
152
+ roundToPrecision,
142
153
  };
@@ -0,0 +1,56 @@
1
+ import { EPSILON } from '../constants';
2
+
3
+ /**
4
+ * Truncates decimal points to that there is at least 1+precision significant
5
+ * digits.
6
+ *
7
+ * For example, with the default precision 2 (3 significant digits)
8
+ * * Values larger than 100 show no information after the decimal point
9
+ * * Values between 10 and 99 show 1 decimal point
10
+ * * Values between 1 and 9 show 2 decimal points
11
+ *
12
+ * @param value - to return a fixed measurement value from
13
+ * @param precision - defining how many digits after 1..9 are desired
14
+ */
15
+ function roundNumber(
16
+ value: string | number | (string | number)[],
17
+ precision = 2
18
+ ): string {
19
+ if (Array.isArray(value)) {
20
+ return value.map((v) => roundNumber(v, precision)).join(', ');
21
+ }
22
+ if (value === undefined || value === null || value === '') {
23
+ return 'NaN';
24
+ }
25
+ value = Number(value);
26
+ if (value < 0.0001) {
27
+ return `${value}`;
28
+ }
29
+ const fixedPrecision =
30
+ value >= 100
31
+ ? precision - 2
32
+ : value >= 10
33
+ ? precision - 1
34
+ : value >= 1
35
+ ? precision
36
+ : value >= 0.1
37
+ ? precision + 1
38
+ : value >= 0.01
39
+ ? precision + 2
40
+ : value >= 0.001
41
+ ? precision + 3
42
+ : precision + 4;
43
+ return value.toFixed(fixedPrecision);
44
+ }
45
+
46
+ /**
47
+ * Rounds a number to the nearest multiple of EPSILON.
48
+ * @param value - The number to round.
49
+ * @returns The rounded number.
50
+ */
51
+ function roundToPrecision(value) {
52
+ return Math.round(value / EPSILON) * EPSILON;
53
+ }
54
+
55
+ export { roundToPrecision };
56
+ export default roundNumber;