@cornerstonejs/core 1.38.1 → 1.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/cjs/utilities/getCurrentVolumeViewportSlice.d.ts +10 -0
  2. package/dist/cjs/utilities/getCurrentVolumeViewportSlice.js +86 -0
  3. package/dist/cjs/utilities/getCurrentVolumeViewportSlice.js.map +1 -0
  4. package/dist/cjs/utilities/index.d.ts +3 -1
  5. package/dist/cjs/utilities/index.js +6 -2
  6. package/dist/cjs/utilities/index.js.map +1 -1
  7. package/dist/cjs/utilities/transformCanvasToIJK.d.ts +2 -0
  8. package/dist/cjs/utilities/transformCanvasToIJK.js +14 -0
  9. package/dist/cjs/utilities/transformCanvasToIJK.js.map +1 -0
  10. package/dist/cjs/utilities/transformIJKToCanvas.d.ts +2 -0
  11. package/dist/cjs/utilities/transformIJKToCanvas.js +14 -0
  12. package/dist/cjs/utilities/transformIJKToCanvas.js.map +1 -0
  13. package/dist/cjs/utilities/transformIndexToWorld.d.ts +2 -0
  14. package/dist/cjs/utilities/transformIndexToWorld.js +7 -0
  15. package/dist/cjs/utilities/transformIndexToWorld.js.map +1 -0
  16. package/dist/esm/utilities/getCurrentVolumeViewportSlice.js +82 -0
  17. package/dist/esm/utilities/getCurrentVolumeViewportSlice.js.map +1 -0
  18. package/dist/esm/utilities/index.js +3 -1
  19. package/dist/esm/utilities/index.js.map +1 -1
  20. package/dist/esm/utilities/transformCanvasToIJK.js +7 -0
  21. package/dist/esm/utilities/transformCanvasToIJK.js.map +1 -0
  22. package/dist/esm/utilities/transformIJKToCanvas.js +7 -0
  23. package/dist/esm/utilities/transformIJKToCanvas.js.map +1 -0
  24. package/dist/esm/utilities/transformIndexToWorld.js +4 -0
  25. package/dist/esm/utilities/transformIndexToWorld.js.map +1 -0
  26. package/dist/types/utilities/getCurrentVolumeViewportSlice.d.ts +11 -0
  27. package/dist/types/utilities/getCurrentVolumeViewportSlice.d.ts.map +1 -0
  28. package/dist/types/utilities/index.d.ts +3 -1
  29. package/dist/types/utilities/index.d.ts.map +1 -1
  30. package/dist/types/utilities/transformCanvasToIJK.d.ts +3 -0
  31. package/dist/types/utilities/transformCanvasToIJK.d.ts.map +1 -0
  32. package/dist/types/utilities/transformIJKToCanvas.d.ts +3 -0
  33. package/dist/types/utilities/transformIJKToCanvas.d.ts.map +1 -0
  34. package/dist/types/utilities/transformIndexToWorld.d.ts +3 -0
  35. package/dist/types/utilities/transformIndexToWorld.d.ts.map +1 -0
  36. package/dist/umd/index.js +1 -1
  37. package/dist/umd/index.js.map +1 -1
  38. package/package.json +2 -2
  39. package/src/utilities/getCurrentVolumeViewportSlice.ts +166 -0
  40. package/src/utilities/index.ts +4 -0
  41. package/src/utilities/transformCanvasToIJK.ts +18 -0
  42. package/src/utilities/transformIJKToCanvas.ts +18 -0
  43. package/src/utilities/transformIndexToWorld.ts +14 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cornerstonejs/core",
3
- "version": "1.38.1",
3
+ "version": "1.39.0",
4
4
  "description": "",
5
5
  "main": "src/index.ts",
6
6
  "types": "dist/types/index.d.ts",
@@ -47,5 +47,5 @@
47
47
  "type": "individual",
48
48
  "url": "https://ohif.org/donate"
49
49
  },
50
- "gitHead": "1b7cb02f6c11935850f80a5382be21dd8aaab4d5"
50
+ "gitHead": "dfec4edcd4585f7a33884517071b77ec2c944cf6"
51
51
  }
@@ -0,0 +1,166 @@
1
+ import { glMatrix, mat4, vec3 } from 'gl-matrix';
2
+ import { IVolumeViewport, Point3 } from '../types';
3
+ import { transformIJKToCanvas } from './transformIJKToCanvas';
4
+ import { transformCanvasToIJK } from './transformCanvasToIJK';
5
+
6
+ /**
7
+ * Get the image data for the current slice rendered on the viewport. The image
8
+ * data returned is the full slice and not only the region that is visible on
9
+ * the viewport. It does not work for oblique views.
10
+ * @param viewport - Volume viewport
11
+ * @returns Slice image dataand matrices to convert from volume
12
+ * to slice and vice-versa
13
+ */
14
+ function getCurrentVolumeViewportSlice(viewport: IVolumeViewport) {
15
+ const { dimensions, scalarData } = viewport.getImageData();
16
+ const { width: canvasWidth, height: canvasHeight } = viewport.getCanvas();
17
+
18
+ // Get three points from the canvas to help us identify the orientation of
19
+ // the slice. Using canvas width/height to get point far away for each other
20
+ // because points such as (0,0), (1,0) and (0,1) may be converted to the same
21
+ // ijk index when the image is zoomed in.
22
+ const ijkOriginPoint = transformCanvasToIJK(viewport, [0, 0]);
23
+ const ijkRowPoint = transformCanvasToIJK(viewport, [canvasWidth - 1, 0]);
24
+ const ijkColPoint = transformCanvasToIJK(viewport, [0, canvasHeight - 1]);
25
+
26
+ // Subtract the points to get the row and column vectors in index space
27
+ const ijkRowVec = vec3.sub(vec3.create(), ijkRowPoint, ijkOriginPoint);
28
+ const ijkColVec = vec3.sub(vec3.create(), ijkColPoint, ijkOriginPoint);
29
+ const ijkSliceVec = vec3.cross(vec3.create(), ijkRowVec, ijkColVec);
30
+
31
+ vec3.normalize(ijkRowVec, ijkRowVec);
32
+ vec3.normalize(ijkColVec, ijkColVec);
33
+ vec3.normalize(ijkSliceVec, ijkSliceVec);
34
+
35
+ // Any unit vector parallel to IJK have one component equal to 1 and
36
+ // the other two components equal to 0. If two of them are parallel
37
+ // the third one is also parallel
38
+ const maxIJKRowVec = Math.max(
39
+ Math.abs(ijkRowVec[0]),
40
+ Math.abs(ijkRowVec[1]),
41
+ Math.abs(ijkRowVec[2])
42
+ );
43
+ const maxIJKColVec = Math.max(
44
+ Math.abs(ijkColVec[0]),
45
+ Math.abs(ijkColVec[1]),
46
+ Math.abs(ijkColVec[2])
47
+ );
48
+
49
+ // Using glMatrix.equals() because the number may be not exactly equal to
50
+ // 1 due to rounding issues
51
+ if (!glMatrix.equals(1, maxIJKRowVec) || !glMatrix.equals(1, maxIJKColVec)) {
52
+ throw new Error('Livewire is not available for rotate/oblique viewports');
53
+ }
54
+
55
+ const [sx, sy, sz] = dimensions;
56
+
57
+ // All eight volume corners in index space
58
+ // prettier-ignore
59
+ const ijkCorners: Point3[] = [
60
+ [ 0, 0, 0], // top-left-front
61
+ [sx - 1, 0, 0], // top-right-front
62
+ [ 0, sy - 1, 0], // bottom-left-front
63
+ [sx - 1, sy - 1, 0], // bottom-right-front
64
+ [ 0, 0, sz - 1], // top-left-back
65
+ [sx - 1, 0, sz - 1], // top-right-back
66
+ [ 0, sy - 1, sz - 1], // bottom-left-back
67
+ [sx - 1, sy - 1, sz - 1], // bottom-right-back
68
+ ];
69
+
70
+ // Project the volume corners onto the canvas
71
+ const canvasCorners = ijkCorners.map((ijkCorner) =>
72
+ transformIJKToCanvas(viewport, ijkCorner)
73
+ );
74
+
75
+ // Calculate the AABB from the corners project onto the canvas
76
+ const canvasAABB = canvasCorners.reduce(
77
+ (aabb, canvasPoint) => {
78
+ aabb.minX = Math.min(aabb.minX, canvasPoint[0]);
79
+ aabb.minY = Math.min(aabb.minY, canvasPoint[1]);
80
+ aabb.maxX = Math.max(aabb.maxX, canvasPoint[0]);
81
+ aabb.maxY = Math.max(aabb.maxY, canvasPoint[1]);
82
+
83
+ return aabb;
84
+ },
85
+ { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity }
86
+ );
87
+
88
+ // Get the top-left, bottom-right and the diagonal vector of
89
+ // the slice in index space
90
+ const ijkTopLeft = transformCanvasToIJK(viewport, [
91
+ canvasAABB.minX,
92
+ canvasAABB.minY,
93
+ ]);
94
+ const ijkBottomRight = transformCanvasToIJK(viewport, [
95
+ canvasAABB.maxX,
96
+ canvasAABB.maxY,
97
+ ]);
98
+ const ijkDiagonal = vec3.sub(vec3.create(), ijkBottomRight, ijkTopLeft);
99
+
100
+ // prettier-ignore
101
+ const sliceToIndexMatrix = mat4.fromValues(
102
+ ijkRowVec[0], ijkRowVec[1], ijkRowVec[2], 0,
103
+ ijkColVec[0], ijkColVec[1], ijkColVec[2], 0,
104
+ ijkSliceVec[0], ijkSliceVec[1], ijkSliceVec[2], 0,
105
+ ijkTopLeft[0], ijkTopLeft[1], ijkTopLeft[2], 1
106
+ );
107
+
108
+ const indexToSliceMatrix = mat4.invert(mat4.create(), sliceToIndexMatrix);
109
+
110
+ // Dot the diagonal with row/column to find the image width/height
111
+ const sliceWidth = vec3.dot(ijkRowVec, ijkDiagonal) + 1;
112
+ const sliceHeight = vec3.dot(ijkColVec, ijkDiagonal) + 1;
113
+
114
+ // Create a TypedArray with same type from the original scalarData
115
+ const TypedArray = (scalarData as any).constructor;
116
+ const sliceData = new TypedArray(sliceWidth * sliceHeight);
117
+
118
+ // We need to know how many pixels to jump for every change in Z direction
119
+ const pixelsPerSlice = dimensions[0] * dimensions[1];
120
+
121
+ // Create two vectors to keep track of each row/column it is, reducing
122
+ // the amount of vec3 instances created and simplifying the math.
123
+ const ijkPixelRow = vec3.clone(ijkTopLeft);
124
+ const ijkPixelCol = vec3.create();
125
+
126
+ // Use an independent index to avoid multiple (x,y) to index conversions
127
+ let slicePixelIndex = 0;
128
+
129
+ for (let y = 0; y < sliceHeight; y++) {
130
+ vec3.copy(ijkPixelCol, ijkPixelRow);
131
+
132
+ for (let x = 0; x < sliceWidth; x++) {
133
+ const volumePixelIndex =
134
+ ijkPixelCol[2] * pixelsPerSlice +
135
+ ijkPixelCol[1] * dimensions[0] +
136
+ ijkPixelCol[0];
137
+
138
+ // It may never throw any "out of bounds" error but just to be safe
139
+ if (volumePixelIndex < scalarData.length) {
140
+ sliceData[slicePixelIndex] = scalarData[volumePixelIndex];
141
+ }
142
+
143
+ // Move to next slice pixel
144
+ slicePixelIndex++;
145
+
146
+ // Move to the next voxel
147
+ vec3.add(ijkPixelCol, ijkPixelCol, ijkRowVec);
148
+ }
149
+
150
+ // Move to the next row
151
+ vec3.add(ijkPixelRow, ijkPixelRow, ijkColVec);
152
+ }
153
+
154
+ return {
155
+ width: sliceWidth,
156
+ height: sliceHeight,
157
+ scalarData: sliceData,
158
+ sliceToIndexMatrix,
159
+ indexToSliceMatrix,
160
+ };
161
+ }
162
+
163
+ export {
164
+ getCurrentVolumeViewportSlice as default,
165
+ getCurrentVolumeViewportSlice,
166
+ };
@@ -26,6 +26,7 @@ import indexWithinDimensions from './indexWithinDimensions';
26
26
  import getVolumeViewportsContainingSameVolumes from './getVolumeViewportsContainingSameVolumes';
27
27
  import getViewportsWithVolumeId from './getViewportsWithVolumeId';
28
28
  import transformWorldToIndex from './transformWorldToIndex';
29
+ import transformIndexToWorld from './transformIndexToWorld';
29
30
  import loadImageToCanvas from './loadImageToCanvas';
30
31
  import renderToCanvasCPU from './renderToCanvasCPU';
31
32
  import renderToCanvasGPU from './renderToCanvasGPU';
@@ -39,6 +40,7 @@ import getImageSliceDataForVolumeViewport from './getImageSliceDataForVolumeView
39
40
  import { isImageActor, actorIsA } from './actorCheck';
40
41
  import getViewportsWithImageURI from './getViewportsWithImageURI';
41
42
  import getClosestStackImageIndexForPoint from './getClosestStackImageIndexForPoint';
43
+ import getCurrentVolumeViewportSlice from './getCurrentVolumeViewportSlice';
42
44
  import calculateViewportsSpatialRegistration from './calculateViewportsSpatialRegistration';
43
45
  import spatialRegistrationMetadataProvider from './spatialRegistrationMetadataProvider';
44
46
  import getViewportImageCornersInWorld from './getViewportImageCornersInWorld';
@@ -98,6 +100,7 @@ export {
98
100
  getVolumeViewportsContainingSameVolumes,
99
101
  getViewportsWithVolumeId,
100
102
  transformWorldToIndex,
103
+ transformIndexToWorld,
101
104
  loadImageToCanvas,
102
105
  renderToCanvasCPU,
103
106
  renderToCanvasGPU,
@@ -113,6 +116,7 @@ export {
113
116
  actorIsA,
114
117
  getViewportsWithImageURI,
115
118
  getClosestStackImageIndexForPoint,
119
+ getCurrentVolumeViewportSlice,
116
120
  calculateViewportsSpatialRegistration,
117
121
  spatialRegistrationMetadataProvider,
118
122
  getViewportImageCornersInWorld,
@@ -0,0 +1,18 @@
1
+ import { IStackViewport, IVolumeViewport, Point2 } from '../types';
2
+ import transformWorldToIndex from './transformWorldToIndex';
3
+
4
+ /**
5
+ * Convert coordinates from canvas to index (volume) space
6
+ * @param viewport - Stack or Volume viewport
7
+ * @param ijkPoint - 2D point in canvas space
8
+ * @returns 3D point in index (volume) space
9
+ */
10
+ export function transformCanvasToIJK(
11
+ viewport: IVolumeViewport | IStackViewport,
12
+ canvasPoint: Point2
13
+ ) {
14
+ const { imageData: vtkImageData } = viewport.getImageData();
15
+ const worldPoint = viewport.canvasToWorld(canvasPoint);
16
+
17
+ return transformWorldToIndex(vtkImageData, worldPoint);
18
+ }
@@ -0,0 +1,18 @@
1
+ import { IStackViewport, IVolumeViewport, Point3 } from '../types';
2
+ import transformIndexToWorld from './transformIndexToWorld';
3
+
4
+ /**
5
+ * Convert coordinates from index (volume) to canvas space
6
+ * @param viewport - Stack or Volume viewport
7
+ * @param ijkPoint - 3D point in index (volume) space
8
+ * @returns 2D point in canvas space
9
+ */
10
+ export function transformIJKToCanvas(
11
+ viewport: IVolumeViewport | IStackViewport,
12
+ ijkPoint: Point3
13
+ ) {
14
+ const { imageData: vtkImageData } = viewport.getImageData();
15
+ const worldPoint = transformIndexToWorld(vtkImageData, ijkPoint);
16
+
17
+ return viewport.worldToCanvas(worldPoint);
18
+ }
@@ -0,0 +1,14 @@
1
+ import type Point3 from '../types/Point3';
2
+
3
+ /**
4
+ * Given an imageData object and a position in voxel space, return a point
5
+ * in world space.
6
+ *
7
+ * @param imageData - The image data object.
8
+ * @param voxelPos - Point in voxel space
9
+ * index space.
10
+ * @returns A point in world space.
11
+ */
12
+ export default function transformIndexToWorld(imageData, voxelPos: Point3) {
13
+ return imageData.indexToWorld(voxelPos);
14
+ }