@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.
- package/dist/cjs/utilities/getCurrentVolumeViewportSlice.d.ts +10 -0
- package/dist/cjs/utilities/getCurrentVolumeViewportSlice.js +86 -0
- package/dist/cjs/utilities/getCurrentVolumeViewportSlice.js.map +1 -0
- package/dist/cjs/utilities/index.d.ts +3 -1
- package/dist/cjs/utilities/index.js +6 -2
- package/dist/cjs/utilities/index.js.map +1 -1
- package/dist/cjs/utilities/transformCanvasToIJK.d.ts +2 -0
- package/dist/cjs/utilities/transformCanvasToIJK.js +14 -0
- package/dist/cjs/utilities/transformCanvasToIJK.js.map +1 -0
- package/dist/cjs/utilities/transformIJKToCanvas.d.ts +2 -0
- package/dist/cjs/utilities/transformIJKToCanvas.js +14 -0
- package/dist/cjs/utilities/transformIJKToCanvas.js.map +1 -0
- package/dist/cjs/utilities/transformIndexToWorld.d.ts +2 -0
- package/dist/cjs/utilities/transformIndexToWorld.js +7 -0
- package/dist/cjs/utilities/transformIndexToWorld.js.map +1 -0
- package/dist/esm/utilities/getCurrentVolumeViewportSlice.js +82 -0
- package/dist/esm/utilities/getCurrentVolumeViewportSlice.js.map +1 -0
- package/dist/esm/utilities/index.js +3 -1
- package/dist/esm/utilities/index.js.map +1 -1
- package/dist/esm/utilities/transformCanvasToIJK.js +7 -0
- package/dist/esm/utilities/transformCanvasToIJK.js.map +1 -0
- package/dist/esm/utilities/transformIJKToCanvas.js +7 -0
- package/dist/esm/utilities/transformIJKToCanvas.js.map +1 -0
- package/dist/esm/utilities/transformIndexToWorld.js +4 -0
- package/dist/esm/utilities/transformIndexToWorld.js.map +1 -0
- package/dist/types/utilities/getCurrentVolumeViewportSlice.d.ts +11 -0
- package/dist/types/utilities/getCurrentVolumeViewportSlice.d.ts.map +1 -0
- package/dist/types/utilities/index.d.ts +3 -1
- package/dist/types/utilities/index.d.ts.map +1 -1
- package/dist/types/utilities/transformCanvasToIJK.d.ts +3 -0
- package/dist/types/utilities/transformCanvasToIJK.d.ts.map +1 -0
- package/dist/types/utilities/transformIJKToCanvas.d.ts +3 -0
- package/dist/types/utilities/transformIJKToCanvas.d.ts.map +1 -0
- package/dist/types/utilities/transformIndexToWorld.d.ts +3 -0
- package/dist/types/utilities/transformIndexToWorld.d.ts.map +1 -0
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +2 -2
- package/src/utilities/getCurrentVolumeViewportSlice.ts +166 -0
- package/src/utilities/index.ts +4 -0
- package/src/utilities/transformCanvasToIJK.ts +18 -0
- package/src/utilities/transformIJKToCanvas.ts +18 -0
- 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.
|
|
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": "
|
|
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
|
+
};
|
package/src/utilities/index.ts
CHANGED
|
@@ -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
|
+
}
|