@cornerstonejs/core 0.36.2 → 0.36.4

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 (233) hide show
  1. package/dist/cjs/loaders/volumeLoader.d.ts +1 -0
  2. package/dist/cjs/loaders/volumeLoader.js +5 -1
  3. package/dist/cjs/loaders/volumeLoader.js.map +1 -1
  4. package/dist/cjs/utilities/getSliceRange.js +3 -1
  5. package/dist/cjs/utilities/getSliceRange.js.map +1 -1
  6. package/dist/cjs/utilities/getTargetVolumeAndSpacingInNormalDir.js +7 -0
  7. package/dist/cjs/utilities/getTargetVolumeAndSpacingInNormalDir.js.map +1 -1
  8. package/dist/esm/loaders/volumeLoader.d.ts +1 -0
  9. package/dist/esm/loaders/volumeLoader.js +3 -0
  10. package/dist/esm/loaders/volumeLoader.js.map +1 -1
  11. package/dist/esm/utilities/getSliceRange.js +3 -1
  12. package/dist/esm/utilities/getSliceRange.js.map +1 -1
  13. package/dist/esm/utilities/getTargetVolumeAndSpacingInNormalDir.js +7 -0
  14. package/dist/esm/utilities/getTargetVolumeAndSpacingInNormalDir.js.map +1 -1
  15. package/dist/umd/index.js +1 -1
  16. package/dist/umd/index.js.map +1 -1
  17. package/package.json +4 -3
  18. package/src/RenderingEngine/BaseVolumeViewport.ts +847 -0
  19. package/src/RenderingEngine/RenderingEngine.ts +1364 -0
  20. package/src/RenderingEngine/StackViewport.ts +2690 -0
  21. package/src/RenderingEngine/Viewport.ts +1244 -0
  22. package/src/RenderingEngine/VolumeViewport.ts +420 -0
  23. package/src/RenderingEngine/VolumeViewport3D.ts +42 -0
  24. package/src/RenderingEngine/getRenderingEngine.ts +34 -0
  25. package/src/RenderingEngine/helpers/addVolumesToViewports.ts +52 -0
  26. package/src/RenderingEngine/helpers/cpuFallback/colors/colormap.ts +343 -0
  27. package/src/RenderingEngine/helpers/cpuFallback/colors/index.ts +4 -0
  28. package/src/RenderingEngine/helpers/cpuFallback/colors/lookupTable.ts +469 -0
  29. package/src/RenderingEngine/helpers/cpuFallback/drawImageSync.ts +58 -0
  30. package/src/RenderingEngine/helpers/cpuFallback/rendering/calculateTransform.ts +136 -0
  31. package/src/RenderingEngine/helpers/cpuFallback/rendering/canvasToPixel.ts +25 -0
  32. package/src/RenderingEngine/helpers/cpuFallback/rendering/computeAutoVoi.ts +47 -0
  33. package/src/RenderingEngine/helpers/cpuFallback/rendering/correctShift.ts +38 -0
  34. package/src/RenderingEngine/helpers/cpuFallback/rendering/createViewport.ts +64 -0
  35. package/src/RenderingEngine/helpers/cpuFallback/rendering/doesImageNeedToBeRendered.ts +36 -0
  36. package/src/RenderingEngine/helpers/cpuFallback/rendering/fitToWindow.ts +22 -0
  37. package/src/RenderingEngine/helpers/cpuFallback/rendering/generateColorLUT.ts +60 -0
  38. package/src/RenderingEngine/helpers/cpuFallback/rendering/generateLut.ts +83 -0
  39. package/src/RenderingEngine/helpers/cpuFallback/rendering/getDefaultViewport.ts +88 -0
  40. package/src/RenderingEngine/helpers/cpuFallback/rendering/getImageFitScale.ts +52 -0
  41. package/src/RenderingEngine/helpers/cpuFallback/rendering/getImageSize.ts +55 -0
  42. package/src/RenderingEngine/helpers/cpuFallback/rendering/getLut.ts +53 -0
  43. package/src/RenderingEngine/helpers/cpuFallback/rendering/getModalityLut.ts +55 -0
  44. package/src/RenderingEngine/helpers/cpuFallback/rendering/getTransform.ts +17 -0
  45. package/src/RenderingEngine/helpers/cpuFallback/rendering/getVOILut.ts +74 -0
  46. package/src/RenderingEngine/helpers/cpuFallback/rendering/initializeRenderCanvas.ts +37 -0
  47. package/src/RenderingEngine/helpers/cpuFallback/rendering/lutMatches.ts +21 -0
  48. package/src/RenderingEngine/helpers/cpuFallback/rendering/now.ts +13 -0
  49. package/src/RenderingEngine/helpers/cpuFallback/rendering/pixelToCanvas.ts +22 -0
  50. package/src/RenderingEngine/helpers/cpuFallback/rendering/renderColorImage.ts +193 -0
  51. package/src/RenderingEngine/helpers/cpuFallback/rendering/renderGrayscaleImage.ts +166 -0
  52. package/src/RenderingEngine/helpers/cpuFallback/rendering/renderPseudoColorImage.ts +203 -0
  53. package/src/RenderingEngine/helpers/cpuFallback/rendering/resetCamera.ts +32 -0
  54. package/src/RenderingEngine/helpers/cpuFallback/rendering/resize.ts +109 -0
  55. package/src/RenderingEngine/helpers/cpuFallback/rendering/saveLastRendered.ts +36 -0
  56. package/src/RenderingEngine/helpers/cpuFallback/rendering/setDefaultViewport.ts +17 -0
  57. package/src/RenderingEngine/helpers/cpuFallback/rendering/setToPixelCoordinateSystem.ts +32 -0
  58. package/src/RenderingEngine/helpers/cpuFallback/rendering/storedColorPixelDataToCanvasImageData.ts +58 -0
  59. package/src/RenderingEngine/helpers/cpuFallback/rendering/storedPixelDataToCanvasImageData.ts +76 -0
  60. package/src/RenderingEngine/helpers/cpuFallback/rendering/storedPixelDataToCanvasImageDataColorLUT.ts +60 -0
  61. package/src/RenderingEngine/helpers/cpuFallback/rendering/storedPixelDataToCanvasImageDataPET.ts +50 -0
  62. package/src/RenderingEngine/helpers/cpuFallback/rendering/storedPixelDataToCanvasImageDataPseudocolorLUT.ts +66 -0
  63. package/src/RenderingEngine/helpers/cpuFallback/rendering/storedPixelDataToCanvasImageDataPseudocolorLUTPET.ts +68 -0
  64. package/src/RenderingEngine/helpers/cpuFallback/rendering/storedPixelDataToCanvasImageDataRGBA.ts +81 -0
  65. package/src/RenderingEngine/helpers/cpuFallback/rendering/storedRGBAPixelDataToCanvasImageData.ts +56 -0
  66. package/src/RenderingEngine/helpers/cpuFallback/rendering/transform.ts +126 -0
  67. package/src/RenderingEngine/helpers/cpuFallback/rendering/validator.ts +31 -0
  68. package/src/RenderingEngine/helpers/createVolumeActor.ts +103 -0
  69. package/src/RenderingEngine/helpers/createVolumeMapper.ts +37 -0
  70. package/src/RenderingEngine/helpers/getOrCreateCanvas.ts +58 -0
  71. package/src/RenderingEngine/helpers/index.ts +15 -0
  72. package/src/RenderingEngine/helpers/isRgbaSourceRgbDest.ts +1 -0
  73. package/src/RenderingEngine/helpers/setDefaultVolumeVOI.ts +227 -0
  74. package/src/RenderingEngine/helpers/setVolumesForViewports.ts +52 -0
  75. package/src/RenderingEngine/helpers/viewportTypeToViewportClass.ts +14 -0
  76. package/src/RenderingEngine/helpers/viewportTypeUsesCustomRenderingPipeline.ts +7 -0
  77. package/src/RenderingEngine/helpers/volumeNewImageEventDispatcher.ts +75 -0
  78. package/src/RenderingEngine/index.ts +23 -0
  79. package/src/RenderingEngine/renderingEngineCache.ts +43 -0
  80. package/src/RenderingEngine/vtkClasses/index.js +11 -0
  81. package/src/RenderingEngine/vtkClasses/vtkOffscreenMultiRenderWindow.js +149 -0
  82. package/src/RenderingEngine/vtkClasses/vtkSharedVolumeMapper.js +52 -0
  83. package/src/RenderingEngine/vtkClasses/vtkSlabCamera.d.ts +781 -0
  84. package/src/RenderingEngine/vtkClasses/vtkSlabCamera.js +155 -0
  85. package/src/RenderingEngine/vtkClasses/vtkStreamingOpenGLRenderWindow.js +47 -0
  86. package/src/RenderingEngine/vtkClasses/vtkStreamingOpenGLTexture.js +272 -0
  87. package/src/RenderingEngine/vtkClasses/vtkStreamingOpenGLViewNodeFactory.js +159 -0
  88. package/src/RenderingEngine/vtkClasses/vtkStreamingOpenGLVolumeMapper.js +319 -0
  89. package/src/Settings.ts +294 -0
  90. package/src/cache/cache.ts +854 -0
  91. package/src/cache/classes/Contour.ts +70 -0
  92. package/src/cache/classes/ContourSet.ts +151 -0
  93. package/src/cache/classes/ImageVolume.ts +155 -0
  94. package/src/cache/index.ts +5 -0
  95. package/src/constants/cpuColormaps.ts +1537 -0
  96. package/src/constants/epsilon.ts +3 -0
  97. package/src/constants/index.ts +13 -0
  98. package/src/constants/mprCameraValues.ts +20 -0
  99. package/src/constants/rendering.ts +8 -0
  100. package/src/constants/viewportPresets.ts +357 -0
  101. package/src/enums/BlendModes.ts +23 -0
  102. package/src/enums/ContourType.ts +6 -0
  103. package/src/enums/Events.ts +196 -0
  104. package/src/enums/GeometryType.ts +5 -0
  105. package/src/enums/InterpolationType.ts +13 -0
  106. package/src/enums/OrientationAxis.ts +8 -0
  107. package/src/enums/RequestType.ts +13 -0
  108. package/src/enums/SharedArrayBufferModes.ts +11 -0
  109. package/src/enums/VOILUTFunctionType.ts +10 -0
  110. package/src/enums/ViewportType.ts +21 -0
  111. package/src/enums/index.ts +23 -0
  112. package/src/eventTarget.ts +67 -0
  113. package/src/getEnabledElement.ts +105 -0
  114. package/src/global.ts +8 -0
  115. package/src/index.ts +123 -0
  116. package/src/init.ts +247 -0
  117. package/src/loaders/geometryLoader.ts +108 -0
  118. package/src/loaders/imageLoader.ts +298 -0
  119. package/src/loaders/volumeLoader.ts +477 -0
  120. package/src/metaData.ts +84 -0
  121. package/src/requestPool/imageLoadPoolManager.ts +43 -0
  122. package/src/requestPool/imageRetrievalPoolManager.ts +25 -0
  123. package/src/requestPool/requestPoolManager.ts +329 -0
  124. package/src/types/ActorSliceRange.ts +17 -0
  125. package/src/types/CPUFallbackColormap.ts +23 -0
  126. package/src/types/CPUFallbackColormapData.ts +12 -0
  127. package/src/types/CPUFallbackColormapsData.ts +7 -0
  128. package/src/types/CPUFallbackEnabledElement.ts +71 -0
  129. package/src/types/CPUFallbackLUT.ts +5 -0
  130. package/src/types/CPUFallbackLookupTable.ts +17 -0
  131. package/src/types/CPUFallbackRenderingTools.ts +25 -0
  132. package/src/types/CPUFallbackTransform.ts +16 -0
  133. package/src/types/CPUFallbackViewport.ts +29 -0
  134. package/src/types/CPUFallbackViewportDisplayedArea.ts +15 -0
  135. package/src/types/CPUIImageData.ts +47 -0
  136. package/src/types/ContourData.ts +19 -0
  137. package/src/types/Cornerstone3DConfig.ts +31 -0
  138. package/src/types/CustomEventType.ts +14 -0
  139. package/src/types/EventTypes.ts +403 -0
  140. package/src/types/FlipDirection.ts +9 -0
  141. package/src/types/IActor.ts +23 -0
  142. package/src/types/ICache.ts +28 -0
  143. package/src/types/ICachedGeometry.ts +13 -0
  144. package/src/types/ICachedImage.ts +13 -0
  145. package/src/types/ICachedVolume.ts +12 -0
  146. package/src/types/ICamera.ts +36 -0
  147. package/src/types/IContour.ts +18 -0
  148. package/src/types/IContourSet.ts +56 -0
  149. package/src/types/IDynamicImageVolume.ts +18 -0
  150. package/src/types/IEnabledElement.ts +21 -0
  151. package/src/types/IGeometry.ts +12 -0
  152. package/src/types/IImage.ts +113 -0
  153. package/src/types/IImageData.ts +45 -0
  154. package/src/types/IImageVolume.ts +78 -0
  155. package/src/types/ILoadObject.ts +36 -0
  156. package/src/types/IRegisterImageLoader.ts +10 -0
  157. package/src/types/IRenderingEngine.ts +28 -0
  158. package/src/types/IStackViewport.ts +138 -0
  159. package/src/types/IStreamingImageVolume.ts +13 -0
  160. package/src/types/IStreamingVolumeProperties.ts +14 -0
  161. package/src/types/IViewport.ts +149 -0
  162. package/src/types/IViewportId.ts +9 -0
  163. package/src/types/IVolume.ts +45 -0
  164. package/src/types/IVolumeInput.ts +36 -0
  165. package/src/types/IVolumeViewport.ts +141 -0
  166. package/src/types/ImageLoaderFn.ts +16 -0
  167. package/src/types/ImageSliceData.ts +6 -0
  168. package/src/types/Mat3.ts +16 -0
  169. package/src/types/Metadata.ts +39 -0
  170. package/src/types/OrientationVectors.ts +36 -0
  171. package/src/types/Plane.ts +6 -0
  172. package/src/types/Point2.ts +6 -0
  173. package/src/types/Point3.ts +6 -0
  174. package/src/types/Point4.ts +6 -0
  175. package/src/types/ScalingParameters.ts +27 -0
  176. package/src/types/StackViewportProperties.ts +25 -0
  177. package/src/types/TransformMatrix2D.ts +4 -0
  178. package/src/types/ViewportInputOptions.ts +21 -0
  179. package/src/types/ViewportPreset.ts +14 -0
  180. package/src/types/VolumeLoaderFn.ts +18 -0
  181. package/src/types/VolumeViewportProperties.ts +14 -0
  182. package/src/types/index.ts +157 -0
  183. package/src/types/voi.ts +15 -0
  184. package/src/utilities/actorCheck.ts +24 -0
  185. package/src/utilities/applyPreset.ts +132 -0
  186. package/src/utilities/calculateViewportsSpatialRegistration.ts +74 -0
  187. package/src/utilities/calibratedPixelSpacingMetadataProvider.ts +38 -0
  188. package/src/utilities/createFloat32SharedArray.ts +45 -0
  189. package/src/utilities/createInt16SharedArray.ts +43 -0
  190. package/src/utilities/createLinearRGBTransferFunction.ts +22 -0
  191. package/src/utilities/createSigmoidRGBTransferFunction.ts +63 -0
  192. package/src/utilities/createUInt16SharedArray.ts +43 -0
  193. package/src/utilities/createUint8SharedArray.ts +45 -0
  194. package/src/utilities/deepFreeze.ts +19 -0
  195. package/src/utilities/deepMerge.ts +81 -0
  196. package/src/utilities/getClosestImageId.ts +80 -0
  197. package/src/utilities/getClosestStackImageIndexForPoint.ts +116 -0
  198. package/src/utilities/getImageSliceDataForVolumeViewport.ts +61 -0
  199. package/src/utilities/getMinMax.ts +31 -0
  200. package/src/utilities/getRuntimeId.ts +54 -0
  201. package/src/utilities/getScalarDataType.ts +31 -0
  202. package/src/utilities/getScalingParameters.ts +35 -0
  203. package/src/utilities/getSliceRange.ts +86 -0
  204. package/src/utilities/getSpacingInNormalDirection.ts +44 -0
  205. package/src/utilities/getTargetVolumeAndSpacingInNormalDir.ts +126 -0
  206. package/src/utilities/getViewportImageCornersInWorld.ts +102 -0
  207. package/src/utilities/getViewportsWithImageURI.ts +46 -0
  208. package/src/utilities/getViewportsWithVolumeId.ts +38 -0
  209. package/src/utilities/getVoiFromSigmoidRGBTransferFunction.ts +23 -0
  210. package/src/utilities/getVolumeActorCorners.ts +24 -0
  211. package/src/utilities/getVolumeSliceRangeInfo.ts +52 -0
  212. package/src/utilities/getVolumeViewportScrollInfo.ts +32 -0
  213. package/src/utilities/getVolumeViewportsContainingSameVolumes.ts +58 -0
  214. package/src/utilities/hasNaNValues.ts +12 -0
  215. package/src/utilities/imageIdToURI.ts +10 -0
  216. package/src/utilities/imageToWorldCoords.ts +54 -0
  217. package/src/utilities/index.ts +100 -0
  218. package/src/utilities/indexWithinDimensions.ts +27 -0
  219. package/src/utilities/invertRgbTransferFunction.ts +36 -0
  220. package/src/utilities/isEqual.ts +27 -0
  221. package/src/utilities/isOpposite.ts +23 -0
  222. package/src/utilities/isTypedArray.ts +20 -0
  223. package/src/utilities/loadImageToCanvas.ts +80 -0
  224. package/src/utilities/planar.ts +91 -0
  225. package/src/utilities/renderToCanvas.ts +32 -0
  226. package/src/utilities/scaleRgbTransferFunction.ts +37 -0
  227. package/src/utilities/snapFocalPointToSlice.ts +78 -0
  228. package/src/utilities/spatialRegistrationMetadataProvider.ts +50 -0
  229. package/src/utilities/transformWorldToIndex.ts +16 -0
  230. package/src/utilities/triggerEvent.ts +38 -0
  231. package/src/utilities/uuidv4.ts +13 -0
  232. package/src/utilities/windowLevel.ts +39 -0
  233. package/src/utilities/worldToImageCoords.ts +64 -0
@@ -0,0 +1,80 @@
1
+ import { vec3 } from 'gl-matrix';
2
+ import * as metaData from '../metaData';
3
+ import type { IImageVolume, Point3 } from '../types';
4
+
5
+ import getSpacingInNormalDirection from './getSpacingInNormalDirection';
6
+
7
+ /**
8
+ * Given an image, a point in space and the viewPlaneNormal it returns the
9
+ * closest imageId of the image volume that is within half voxel spacing
10
+ * of the point in space.
11
+ * @param imageVolume - The image volume
12
+ * @param worldPos - The position in the world coordinate system (from mouse click)
13
+ * @param viewPlaneNormal - The normal vector of the viewport
14
+ * @param viewUp - The viewUp vector of the camera.
15
+ *
16
+ * @returns The imageId for the tool.
17
+ */
18
+ export default function getClosestImageId(
19
+ imageVolume: IImageVolume,
20
+ worldPos: Point3,
21
+ viewPlaneNormal: Point3,
22
+ viewUp: Point3
23
+ ): string {
24
+ if (!imageVolume) {
25
+ return;
26
+ }
27
+
28
+ const { direction, imageIds } = imageVolume;
29
+
30
+ if (!imageIds || !imageIds.length) {
31
+ return;
32
+ }
33
+
34
+ // 1. Get ScanAxis vector
35
+ const kVector = direction.slice(6, 9);
36
+
37
+ // 2. Check if scanAxis is not parallel to camera viewPlaneNormal
38
+ const dotProducts = vec3.dot(kVector as Point3, <vec3>viewPlaneNormal);
39
+
40
+ // 2.a if imagePlane is not parallel to the camera: tool is not drawn on an
41
+ // imaging plane, return
42
+ if (Math.abs(dotProducts) < 0.99) {
43
+ return;
44
+ }
45
+
46
+ // 3. Calculate Spacing the in the normal direction, this will get used to
47
+ // check whether we are withing a slice
48
+ const spacingInNormalDirection = getSpacingInNormalDirection(
49
+ imageVolume,
50
+ viewPlaneNormal
51
+ );
52
+
53
+ const halfSpacingInNormalDirection = spacingInNormalDirection / 2;
54
+
55
+ // 4. Iterate over all imageIds and check if the tool point (worldPos) is
56
+ // withing one of the slices defined by an imageId
57
+ let imageIdForTool;
58
+ for (let i = 0; i < imageIds.length; i++) {
59
+ const imageId = imageIds[i];
60
+
61
+ // 4.a Get metadata for the imageId
62
+ const { imagePositionPatient } = metaData.get('imagePlaneModule', imageId);
63
+
64
+ // 4.b Calculate the direction vector from annotation. point to the first voxel
65
+ // of this image defined by imageId
66
+ const dir = vec3.create();
67
+ vec3.sub(dir, worldPos, imagePositionPatient);
68
+
69
+ // 4.c Calculate the distance between the vector above and the viewplaneNormal
70
+ // i.e., projected distance
71
+ const dot = vec3.dot(dir, viewPlaneNormal);
72
+
73
+ // 4.d If the distance is withing range, return the imageId
74
+ if (Math.abs(dot) < halfSpacingInNormalDirection) {
75
+ imageIdForTool = imageId;
76
+ }
77
+ }
78
+
79
+ return imageIdForTool;
80
+ }
@@ -0,0 +1,116 @@
1
+ import { vec3 } from 'gl-matrix';
2
+ import { planar } from '.';
3
+ import { metaData } from '..';
4
+ import { IStackViewport, Point3 } from '../types';
5
+
6
+ /**
7
+ * Given a point in 3D space and a viewport it returns the index of the closest imageId, it assumes that stack images are sorted according to their sliceLocation
8
+ * @param point - [A, B, C] coordinates of the point in 3D space
9
+ * @param viewport - The StackViewport to search for the closest imageId
10
+ *
11
+ * @returns The imageId index of the closest imageId or null if no imageId is found
12
+ */
13
+ export default function getClosestStackImageIndexForPoint(
14
+ point: Point3,
15
+ viewport: IStackViewport
16
+ ): number | null {
17
+ const minimalDistance = calculateMinimalDistanceForStackViewport(
18
+ point,
19
+ viewport
20
+ );
21
+ return minimalDistance ? minimalDistance.index : null;
22
+ }
23
+
24
+ //assumes that imageIds are sorted by slice location
25
+ export function calculateMinimalDistanceForStackViewport(
26
+ point: Point3,
27
+ viewport: IStackViewport
28
+ ): { distance: number; index: number } | null {
29
+ const imageIds = viewport.getImageIds();
30
+ const currentImageIdIndex = viewport.getCurrentImageIdIndex();
31
+
32
+ if (imageIds.length === 0) return null;
33
+
34
+ const getDistance = (imageId: string): null | number => {
35
+ const planeMetadata = getPlaneMetadata(imageId);
36
+ if (!planeMetadata) return null;
37
+ const plane = planar.planeEquation(
38
+ planeMetadata.planeNormal,
39
+ planeMetadata.imagePositionPatient
40
+ );
41
+ const distance = planar.planeDistanceToPoint(plane, point);
42
+ return distance;
43
+ };
44
+
45
+ const closestStack = {
46
+ distance: getDistance(imageIds[currentImageIdIndex]) ?? Infinity,
47
+ index: currentImageIdIndex,
48
+ };
49
+
50
+ //check higher indices
51
+ const higherImageIds = imageIds.slice(currentImageIdIndex + 1);
52
+
53
+ for (let i = 0; i < higherImageIds.length; i++) {
54
+ const id = higherImageIds[i];
55
+ const distance = getDistance(id);
56
+ if (distance === null) continue;
57
+ if (distance <= closestStack.distance) {
58
+ closestStack.distance = distance;
59
+ closestStack.index = i + currentImageIdIndex + 1;
60
+ } else break;
61
+ }
62
+ //check lower indices
63
+ const lowerImageIds = imageIds.slice(0, currentImageIdIndex);
64
+ for (let i = lowerImageIds.length - 1; i >= 0; i--) {
65
+ const id = lowerImageIds[i];
66
+ const distance = getDistance(id);
67
+ if (distance === null || distance === closestStack.distance) continue;
68
+ if (distance < closestStack.distance) {
69
+ closestStack.distance = distance;
70
+ closestStack.index = i;
71
+ } else break;
72
+ }
73
+ return closestStack.distance === Infinity ? null : closestStack;
74
+ }
75
+
76
+ function getPlaneMetadata(imageId: string): null | {
77
+ rowCosines: Point3;
78
+ columnCosines: Point3;
79
+ imagePositionPatient: Point3;
80
+ planeNormal: Point3;
81
+ } {
82
+ const targetImagePlane = metaData.get('imagePlaneModule', imageId);
83
+
84
+ if (
85
+ !targetImagePlane ||
86
+ !(
87
+ targetImagePlane.rowCosines instanceof Array &&
88
+ targetImagePlane.rowCosines.length === 3
89
+ ) ||
90
+ !(
91
+ targetImagePlane.columnCosines instanceof Array &&
92
+ targetImagePlane.columnCosines.length === 3
93
+ ) ||
94
+ !(
95
+ targetImagePlane.imagePositionPatient instanceof Array &&
96
+ targetImagePlane.imagePositionPatient.length === 3
97
+ )
98
+ ) {
99
+ return null;
100
+ }
101
+ const {
102
+ rowCosines,
103
+ columnCosines,
104
+ imagePositionPatient,
105
+ }: {
106
+ rowCosines: Point3;
107
+ columnCosines: Point3;
108
+ imagePositionPatient: Point3;
109
+ } = targetImagePlane;
110
+
111
+ const rowVec = vec3.set(vec3.create(), ...rowCosines);
112
+ const colVec = vec3.set(vec3.create(), ...columnCosines);
113
+ const planeNormal = vec3.cross(vec3.create(), rowVec, colVec) as Point3;
114
+
115
+ return { rowCosines, columnCosines, imagePositionPatient, planeNormal };
116
+ }
@@ -0,0 +1,61 @@
1
+ import { ImageSliceData, IVolumeViewport, VolumeActor } from '../types';
2
+ import getSliceRange from './getSliceRange';
3
+ import getTargetVolumeAndSpacingInNormalDir from './getTargetVolumeAndSpacingInNormalDir';
4
+
5
+ /**
6
+ * It calculates the number of slices and the current slice index for a given
7
+ * Volume viewport
8
+ * @param viewport - volume viewport
9
+ * @returns An object with two properties: numberOfSlices and imageIndex.
10
+ */
11
+ function getImageSliceDataForVolumeViewport(
12
+ viewport: IVolumeViewport
13
+ ): ImageSliceData {
14
+ const camera = viewport.getCamera();
15
+
16
+ const { spacingInNormalDirection, imageVolume } =
17
+ getTargetVolumeAndSpacingInNormalDir(viewport, camera);
18
+
19
+ if (!imageVolume) {
20
+ return;
21
+ }
22
+
23
+ const { viewPlaneNormal, focalPoint } = camera;
24
+
25
+ const actorEntry = viewport
26
+ .getActors()
27
+ .find(
28
+ (a) =>
29
+ a.referenceId === imageVolume.volumeId || a.uid === imageVolume.volumeId
30
+ );
31
+
32
+ if (!actorEntry) {
33
+ console.warn('No actor found for with actorUID of', imageVolume.volumeId);
34
+ }
35
+
36
+ const volumeActor = actorEntry.actor as VolumeActor;
37
+ const sliceRange = getSliceRange(volumeActor, viewPlaneNormal, focalPoint);
38
+
39
+ const { min, max, current } = sliceRange;
40
+
41
+ // calculate number of steps from min to max with current normal spacing in direction
42
+ const numberOfSlices = Math.round((max - min) / spacingInNormalDirection) + 1;
43
+
44
+ // calculate the imageIndex based on min, max, current
45
+ let imageIndex = ((current - min) / (max - min)) * numberOfSlices;
46
+ imageIndex = Math.floor(imageIndex);
47
+
48
+ // Clamp imageIndex
49
+ if (imageIndex > numberOfSlices - 1) {
50
+ imageIndex = numberOfSlices - 1;
51
+ } else if (imageIndex < 0) {
52
+ imageIndex = 0;
53
+ }
54
+
55
+ return {
56
+ numberOfSlices,
57
+ imageIndex,
58
+ };
59
+ }
60
+
61
+ export default getImageSliceDataForVolumeViewport;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Calculate the minimum and maximum values in an Array
3
+ *
4
+ * @param storedPixelData - The pixel data to calculate the min and max values for
5
+ * @returns an object with two properties: min and max
6
+ */
7
+ export default function getMinMax(storedPixelData: number[]): {
8
+ min: number;
9
+ max: number;
10
+ } {
11
+ // we always calculate the min max values since they are not always
12
+ // present in DICOM and we don't want to trust them anyway as cornerstone
13
+ // depends on us providing reliable values for these
14
+ let min = storedPixelData[0];
15
+
16
+ let max = storedPixelData[0];
17
+
18
+ let storedPixel;
19
+ const numPixels = storedPixelData.length;
20
+
21
+ for (let index = 1; index < numPixels; index++) {
22
+ storedPixel = storedPixelData[index];
23
+ min = Math.min(min, storedPixel);
24
+ max = Math.max(max, storedPixel);
25
+ }
26
+
27
+ return {
28
+ min,
29
+ max,
30
+ };
31
+ }
@@ -0,0 +1,54 @@
1
+ const LAST_RUNTIME_ID = Symbol('LastRuntimeId');
2
+ const GLOBAL_CONTEXT = {};
3
+ const DEFAULT_MAX = 0xffffffff; // Max 32-bit integer
4
+ const DEFAULT_SEPARATOR = '-';
5
+
6
+ /**
7
+ * Generate a unique numeric ID string valid during a single runtime session;
8
+ *
9
+ * @param context - An optional object to be used as context.
10
+ * Defaults to a global context;
11
+ * @param separator - The component separator. Defaults to "-";
12
+ * @param max - The maximum component value. Defaults to 4294967295;
13
+ * @returns The string representation of the the unique ID;
14
+ */
15
+ export default function getRuntimeId(
16
+ context?: unknown,
17
+ separator?: string,
18
+ max?: number
19
+ ): string {
20
+ return getNextRuntimeId(
21
+ // @ts-ignore
22
+ context !== null && typeof context === 'object' ? context : GLOBAL_CONTEXT,
23
+ LAST_RUNTIME_ID,
24
+ (typeof max === 'number' && max > 0 ? max : DEFAULT_MAX) >>> 0
25
+ ).join(typeof separator === 'string' ? separator : DEFAULT_SEPARATOR);
26
+ }
27
+
28
+ /*
29
+ * Helpers
30
+ */
31
+
32
+ function getNextRuntimeId(
33
+ context: Record<symbol, Array<number>>,
34
+ symbol: symbol,
35
+ max: number
36
+ ): Array<number> {
37
+ let idComponents = context[symbol];
38
+ if (!(idComponents instanceof Array)) {
39
+ idComponents = [0];
40
+ Object.defineProperty(context, symbol, { value: idComponents });
41
+ }
42
+ for (let carry = true, i = 0; carry && i < idComponents.length; ++i) {
43
+ let n = idComponents[i] | 0;
44
+ if (n < max) {
45
+ carry = false;
46
+ n = n + 1;
47
+ } else {
48
+ n = 0;
49
+ if (i + 1 === idComponents.length) idComponents.push(0);
50
+ }
51
+ idComponents[i] = n;
52
+ }
53
+ return idComponents;
54
+ }
@@ -0,0 +1,31 @@
1
+ import { ScalingParameters } from '../types';
2
+
3
+ /**
4
+ * If the scalar data is a Uint8Array, return 'Uint8Array'. If the scalar data is a
5
+ * Float32Array, return 'Float32Array'. If the scalar data is a Int16Array, return
6
+ * 'Int16Array'. If the scalar data is a Uint16Array, return 'Uint16Array'. If the
7
+ * scalar data is none of the above, throw an error.
8
+ * @param {ScalingParameters} scalingParameters - {
9
+ * @param {any} [scalarData] - The data to be converted.
10
+ * @returns The data type of the scalar data.
11
+ */
12
+ export default function getScalarDataType(
13
+ scalingParameters: ScalingParameters,
14
+ scalarData?: any
15
+ ): string {
16
+ let type;
17
+
18
+ if (scalarData && scalarData instanceof Uint8Array) {
19
+ type = 'Uint8Array';
20
+ } else if (scalarData instanceof Float32Array) {
21
+ type = 'Float32Array';
22
+ } else if (scalarData instanceof Int16Array) {
23
+ type = 'Int16Array';
24
+ } else if (scalarData instanceof Uint16Array) {
25
+ type = 'Uint16Array';
26
+ } else {
27
+ throw new Error('Unsupported array type');
28
+ }
29
+
30
+ return type;
31
+ }
@@ -0,0 +1,35 @@
1
+ import { get as metaDataGet } from '../metaData';
2
+ import { ScalingParameters } from '../types';
3
+
4
+ /**
5
+ * It returns the scaling parameters for the image with the given imageId. This can be
6
+ * used to get passed (as an option) to the imageLoader in order to apply scaling to the image inside
7
+ * the imageLoader.
8
+ * @param imageId - The imageId of the image
9
+ * @returns ScalingParameters
10
+ */
11
+ export default function getScalingParameters(
12
+ imageId: string
13
+ ): ScalingParameters {
14
+ const modalityLutModule = metaDataGet('modalityLutModule', imageId) || {};
15
+ const generalSeriesModule = metaDataGet('generalSeriesModule', imageId) || {};
16
+
17
+ const { modality } = generalSeriesModule;
18
+
19
+ const scalingParameters = {
20
+ rescaleSlope: modalityLutModule.rescaleSlope,
21
+ rescaleIntercept: modalityLutModule.rescaleIntercept,
22
+ modality,
23
+ };
24
+
25
+ const suvFactor = metaDataGet('scalingModule', imageId) || {};
26
+
27
+ return {
28
+ ...scalingParameters,
29
+ ...(modality === 'PT' && {
30
+ suvbw: suvFactor.suvbw,
31
+ suvbsa: suvFactor.suvbsa,
32
+ suvlbm: suvFactor.suvlbm,
33
+ }),
34
+ };
35
+ }
@@ -0,0 +1,86 @@
1
+ import vtkMatrixBuilder from '@kitware/vtk.js/Common/Core/MatrixBuilder';
2
+ import getVolumeActorCorners from './getVolumeActorCorners';
3
+ import type { VolumeActor, Point3, ActorSliceRange } from '../types';
4
+ import { EPSILON } from '../constants';
5
+
6
+ const SMALL_EPSILON = EPSILON * EPSILON;
7
+ const isOne = (v) => Math.abs(Math.abs(v) - 1) < SMALL_EPSILON;
8
+ const isUnit = (v, off) =>
9
+ isOne(v[off]) || isOne(v[off + 1]) || isOne(v[off + 2]);
10
+
11
+ const isOrthonormal = (v) => isUnit(v, 0) && isUnit(v, 3) && isUnit(v, 6);
12
+
13
+ /**
14
+ * Given a `vtkVolumeActor`, and a normal direction,
15
+ * calculate the range of slices in the focal normal direction that encapsulate
16
+ * the volume. Also project the `focalPoint` onto this range.
17
+ *
18
+ * @param volumeActor - The `vtkVolumeActor`.
19
+ * @param viewPlaneNormal - The normal to the camera view.
20
+ * @param focalPoint - The focal point of the camera.
21
+ *
22
+ * @returns an object containing the `min`, `max` and `current`
23
+ * positions in the normal direction.
24
+ */
25
+ export default function getSliceRange(
26
+ volumeActor: VolumeActor,
27
+ viewPlaneNormal: Point3,
28
+ focalPoint: Point3
29
+ ): ActorSliceRange {
30
+ const imageData = volumeActor.getMapper().getInputData();
31
+ let corners;
32
+ const direction = imageData.getDirection();
33
+
34
+ if (isOrthonormal(direction)) {
35
+ // This logic is only valid when the IJK vectors are unit vectors
36
+ corners = getVolumeActorCorners(volumeActor);
37
+ } else {
38
+ // This logic works for both unit and non-unit vectors, but is slower
39
+ const [dx, dy, dz] = imageData.getDimensions();
40
+ const cornersIdx = [
41
+ [0, 0, 0],
42
+ [dx - 1, 0, 0],
43
+ [0, dy - 1, 0],
44
+ [dx - 1, dy - 1, 0],
45
+ [0, 0, dz - 1],
46
+ [dx - 1, 0, dz - 1],
47
+ [0, dy - 1, dz - 1],
48
+ [dx - 1, dy - 1, dz - 1],
49
+ ];
50
+ corners = cornersIdx.map((it) => imageData.indexToWorld(it));
51
+ }
52
+ // Get rotation matrix from normal to +X (since bounds is aligned to XYZ)
53
+ const transform = vtkMatrixBuilder
54
+ .buildFromDegree()
55
+ .identity()
56
+ .rotateFromDirections(viewPlaneNormal, [1, 0, 0]);
57
+
58
+ corners.forEach((pt) => transform.apply(pt));
59
+
60
+ const transformedFocalPoint = [...focalPoint];
61
+ transform.apply(transformedFocalPoint);
62
+
63
+ const currentSlice = transformedFocalPoint[0];
64
+
65
+ // range is now maximum X distance
66
+ let minX = Infinity;
67
+ let maxX = -Infinity;
68
+ for (let i = 0; i < 8; i++) {
69
+ const x = corners[i][0];
70
+ if (x > maxX) {
71
+ maxX = x;
72
+ }
73
+ if (x < minX) {
74
+ minX = x;
75
+ }
76
+ }
77
+
78
+ return {
79
+ min: minX,
80
+ max: maxX,
81
+ current: currentSlice,
82
+ actor: volumeActor,
83
+ viewPlaneNormal,
84
+ focalPoint,
85
+ };
86
+ }
@@ -0,0 +1,44 @@
1
+ import { vec3 } from 'gl-matrix';
2
+ import { IImageVolume, Point3 } from '../types';
3
+
4
+ /**
5
+ * Given an `imageVolume` and a normal direction (`viewPlaneNormal`), calculates
6
+ * the spacing between voxels in the normal direction. If (`viewPlaneNormal`) is
7
+ * parallel to one of the directions you will obtain the spacing in that direction.
8
+ * Otherwise each of the `imageVolume`'s directions are projected onto the volume,
9
+ * so that you obtain a spacing of the order of "seeing a new set of voxels if the camera where to dolly".
10
+ *
11
+ * @param imageVolume - The image volume to calculate the spacing in the normal direction.
12
+ * @param viewPlaneNormal - The normal direction of the view plane.
13
+ * @returns
14
+ */
15
+ export default function getSpacingInNormalDirection(
16
+ imageVolume: IImageVolume,
17
+ viewPlaneNormal: Point3
18
+ ): number {
19
+ const { direction, spacing } = imageVolume;
20
+
21
+ // Calculate size of spacing vector in normal direction
22
+ const iVector = direction.slice(0, 3) as Point3;
23
+ const jVector = direction.slice(3, 6) as Point3;
24
+ const kVector = direction.slice(6, 9) as Point3;
25
+
26
+ const dotProducts = [
27
+ vec3.dot(iVector, <vec3>viewPlaneNormal),
28
+ vec3.dot(jVector, <vec3>viewPlaneNormal),
29
+ vec3.dot(kVector, <vec3>viewPlaneNormal),
30
+ ];
31
+
32
+ const projectedSpacing = vec3.create();
33
+
34
+ vec3.set(
35
+ projectedSpacing,
36
+ dotProducts[0] * spacing[0],
37
+ dotProducts[1] * spacing[1],
38
+ dotProducts[2] * spacing[2]
39
+ );
40
+
41
+ const spacingInNormalDirection = vec3.length(projectedSpacing);
42
+
43
+ return spacingInNormalDirection;
44
+ }
@@ -0,0 +1,126 @@
1
+ import cache from '../cache/cache';
2
+ import { EPSILON } from '../constants';
3
+ // import type { VolumeViewport } from '../RenderingEngine'
4
+ import { ICamera, IImageVolume, IVolumeViewport } from '../types';
5
+ import getSpacingInNormalDirection from './getSpacingInNormalDirection';
6
+ import { getVolumeLoaderSchemes } from '../loaders/volumeLoader';
7
+
8
+ // One EPSILON part larger multiplier
9
+ const EPSILON_PART = 1 + EPSILON;
10
+
11
+ const startsWith = (str, starts) =>
12
+ starts === str.substring(0, Math.min(str.length, starts.length));
13
+
14
+ // Check if this is a primary volume
15
+ // For now, that means it came from some sort of image loader, but
16
+ // should be specifically designated.
17
+ const isPrimaryVolume = (volume): boolean =>
18
+ !!getVolumeLoaderSchemes().find((scheme) =>
19
+ startsWith(volume.volumeId, scheme)
20
+ );
21
+
22
+ /**
23
+ * Given a volume viewport and camera, find the target volume.
24
+ * The imageVolume is retrieved from cache for the specified targetVolumeId or
25
+ * in case it is not provided, it chooses the volumeId on the viewport (there
26
+ * might be more than one in case of fusion) that has the finest resolution in the
27
+ * direction of view (normal).
28
+ *
29
+ * @param viewport - volume viewport
30
+ * @param camera - current camera
31
+ * @param targetVolumeId - If a target volumeId is given that volume
32
+ * is forced to be used.
33
+ *
34
+ * @returns An object containing the imageVolume and spacingInNormalDirection.
35
+ *
36
+ */
37
+ export default function getTargetVolumeAndSpacingInNormalDir(
38
+ viewport: IVolumeViewport,
39
+ camera: ICamera,
40
+ targetVolumeId?: string
41
+ ): {
42
+ imageVolume: IImageVolume;
43
+ spacingInNormalDirection: number;
44
+ actorUID: string;
45
+ } {
46
+ const { viewPlaneNormal } = camera;
47
+ const volumeActors = viewport.getActors();
48
+
49
+ if (!volumeActors || !volumeActors.length) {
50
+ return {
51
+ spacingInNormalDirection: null,
52
+ imageVolume: null,
53
+ actorUID: null,
54
+ };
55
+ }
56
+
57
+ const imageVolumes = volumeActors
58
+ .map((va) => {
59
+ // prefer the referenceUID if it is set, since it can be a derived actor
60
+ // and the uid does not necessarily match the volumeId
61
+ const actorUID = va.referenceId ?? va.uid;
62
+ return cache.getVolume(actorUID);
63
+ })
64
+ .filter((iv) => !!iv);
65
+
66
+ // If a volumeId is defined, set that volume as the target
67
+ if (targetVolumeId) {
68
+ const imageVolumeIndex = imageVolumes.findIndex(
69
+ (iv) => iv.volumeId === targetVolumeId
70
+ );
71
+
72
+ const imageVolume = imageVolumes[imageVolumeIndex];
73
+ const { uid: actorUID } = volumeActors[imageVolumeIndex];
74
+ const spacingInNormalDirection = getSpacingInNormalDirection(
75
+ imageVolume,
76
+ viewPlaneNormal
77
+ );
78
+
79
+ return { imageVolume, spacingInNormalDirection, actorUID };
80
+ }
81
+
82
+ if (!imageVolumes.length) {
83
+ return {
84
+ spacingInNormalDirection: null,
85
+ imageVolume: null,
86
+ actorUID: null,
87
+ };
88
+ }
89
+
90
+ // Fetch volume actor with finest resolution in direction of projection.
91
+ const smallest = {
92
+ spacingInNormalDirection: Infinity,
93
+ imageVolume: null,
94
+ actorUID: null,
95
+ };
96
+
97
+ const hasPrimaryVolume = imageVolumes.find(isPrimaryVolume);
98
+
99
+ for (let i = 0; i < imageVolumes.length; i++) {
100
+ const imageVolume = imageVolumes[i];
101
+
102
+ if (hasPrimaryVolume && !isPrimaryVolume(imageVolume)) {
103
+ // Secondary volumes like segmentation don't count towards spacing
104
+ continue;
105
+ }
106
+
107
+ const spacingInNormalDirection = getSpacingInNormalDirection(
108
+ imageVolume,
109
+ viewPlaneNormal
110
+ );
111
+
112
+ // Allow for EPSILON part larger requirement to prefer earlier volumes
113
+ // when the spacing is within a factor of EPSILON. Use a factor because
114
+ // that deals with very small or very large volumes effectively.
115
+ if (
116
+ spacingInNormalDirection * EPSILON_PART <
117
+ smallest.spacingInNormalDirection
118
+ ) {
119
+ smallest.spacingInNormalDirection = spacingInNormalDirection;
120
+ smallest.imageVolume = imageVolume;
121
+ smallest.actorUID = volumeActors[i].uid;
122
+ }
123
+ }
124
+
125
+ return smallest;
126
+ }