@cornerstonejs/tools 1.43.5 → 1.43.7
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/stateManagement/annotation/annotationState.js +3 -2
- package/dist/cjs/stateManagement/annotation/annotationState.js.map +1 -1
- package/dist/cjs/stateManagement/annotation/helpers/state.js +5 -6
- package/dist/cjs/stateManagement/annotation/helpers/state.js.map +1 -1
- package/dist/cjs/types/EventTypes.d.ts +2 -2
- package/dist/cjs/utilities/pointInSurroundingSphereCallback.d.ts +4 -0
- package/dist/cjs/utilities/pointInSurroundingSphereCallback.js +70 -0
- package/dist/cjs/utilities/pointInSurroundingSphereCallback.js.map +1 -0
- package/dist/esm/stateManagement/annotation/annotationState.js +3 -2
- package/dist/esm/stateManagement/annotation/annotationState.js.map +1 -1
- package/dist/esm/stateManagement/annotation/helpers/state.js +5 -6
- package/dist/esm/stateManagement/annotation/helpers/state.js.map +1 -1
- package/dist/esm/utilities/pointInSurroundingSphereCallback.js +64 -0
- package/dist/esm/utilities/pointInSurroundingSphereCallback.js.map +1 -0
- package/dist/types/stateManagement/annotation/helpers/state.d.ts.map +1 -1
- package/dist/types/types/EventTypes.d.ts +2 -2
- package/dist/types/types/EventTypes.d.ts.map +1 -1
- package/dist/types/utilities/pointInSurroundingSphereCallback.d.ts +5 -0
- package/dist/types/utilities/pointInSurroundingSphereCallback.d.ts.map +1 -0
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +3 -3
- package/src/stateManagement/annotation/annotationState.ts +3 -3
- package/src/stateManagement/annotation/helpers/state.ts +6 -10
- package/src/types/EventTypes.ts +2 -2
- package/src/utilities/pointInSurroundingSphereCallback.ts +188 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cornerstonejs/tools",
|
|
3
|
-
"version": "1.43.
|
|
3
|
+
"version": "1.43.7",
|
|
4
4
|
"description": "Cornerstone3D Tools",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "dist/types/index.d.ts",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"webpack:watch": "webpack --mode development --progress --watch --config ./.webpack/webpack.dev.js"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@cornerstonejs/core": "^1.43.
|
|
32
|
+
"@cornerstonejs/core": "^1.43.7",
|
|
33
33
|
"comlink": "^4.4.1",
|
|
34
34
|
"lodash.clonedeep": "4.5.0",
|
|
35
35
|
"lodash.get": "^4.4.2"
|
|
@@ -53,5 +53,5 @@
|
|
|
53
53
|
"type": "individual",
|
|
54
54
|
"url": "https://ohif.org/donate"
|
|
55
55
|
},
|
|
56
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "2d0c559929aa0524bf303922230ded50ac0ef1df"
|
|
57
57
|
}
|
|
@@ -80,18 +80,18 @@ function addAnnotation(
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
const manager = getAnnotationManager();
|
|
83
|
-
const groupKey = manager.getGroupKey(annotationGroupSelector);
|
|
84
|
-
|
|
85
|
-
manager.addAnnotation(annotation, groupKey);
|
|
86
83
|
|
|
87
84
|
// if the annotation manager selector is an element, trigger the
|
|
88
85
|
// annotation added event for that element.
|
|
89
86
|
if (annotationGroupSelector instanceof HTMLDivElement) {
|
|
87
|
+
const groupKey = manager.getGroupKey(annotationGroupSelector);
|
|
88
|
+
manager.addAnnotation(annotation, groupKey);
|
|
90
89
|
triggerAnnotationAddedForElement(annotation, annotationGroupSelector);
|
|
91
90
|
} else {
|
|
92
91
|
// if no element is provided, render all viewports that have the
|
|
93
92
|
// same frame of reference.
|
|
94
93
|
// Todo: we should do something else here for other types of annotation managers.
|
|
94
|
+
manager.addAnnotation(annotation);
|
|
95
95
|
triggerAnnotationAddedForFOR(annotation);
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -41,14 +41,12 @@ function triggerAnnotationAddedForFOR(annotation: Annotation) {
|
|
|
41
41
|
const { toolName } = annotation.metadata;
|
|
42
42
|
|
|
43
43
|
const toolGroups = getToolGroupsWithToolName(toolName);
|
|
44
|
-
|
|
45
44
|
if (!toolGroups.length) {
|
|
46
45
|
return;
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
// Find the viewports in the toolGroups who has the same FrameOfReferenceUID
|
|
50
49
|
const viewportsToRender = [];
|
|
51
|
-
|
|
52
50
|
toolGroups.forEach((toolGroup) => {
|
|
53
51
|
toolGroup.viewportsInfo.forEach((viewportInfo) => {
|
|
54
52
|
const { renderingEngineId, viewportId } = viewportInfo;
|
|
@@ -63,19 +61,17 @@ function triggerAnnotationAddedForFOR(annotation: Annotation) {
|
|
|
63
61
|
});
|
|
64
62
|
});
|
|
65
63
|
|
|
64
|
+
const eventType = Events.ANNOTATION_ADDED;
|
|
65
|
+
const eventDetail: AnnotationAddedEventDetail = { annotation };
|
|
66
|
+
|
|
66
67
|
if (!viewportsToRender.length) {
|
|
68
|
+
triggerEvent(eventTarget, eventType, eventDetail);
|
|
67
69
|
return;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
|
-
const eventType = Events.ANNOTATION_ADDED;
|
|
71
|
-
|
|
72
72
|
viewportsToRender.forEach(({ renderingEngineId, viewportId }) => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
viewportId,
|
|
76
|
-
renderingEngineId,
|
|
77
|
-
};
|
|
78
|
-
|
|
73
|
+
eventDetail.viewportId = viewportId;
|
|
74
|
+
eventDetail.renderingEngineId = renderingEngineId;
|
|
79
75
|
triggerEvent(eventTarget, eventType, eventDetail);
|
|
80
76
|
});
|
|
81
77
|
}
|
package/src/types/EventTypes.ts
CHANGED
|
@@ -104,9 +104,9 @@ type ToolActivatedEventDetail = {
|
|
|
104
104
|
*/
|
|
105
105
|
type AnnotationAddedEventDetail = {
|
|
106
106
|
/** unique id of the viewport */
|
|
107
|
-
viewportId
|
|
107
|
+
viewportId?: string;
|
|
108
108
|
/** unique id of the rendering engine */
|
|
109
|
-
renderingEngineId
|
|
109
|
+
renderingEngineId?: string;
|
|
110
110
|
/** The annotation that is being added to the annotations manager. */
|
|
111
111
|
annotation: Annotation;
|
|
112
112
|
};
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { utilities as csUtils } from '@cornerstonejs/core';
|
|
2
|
+
import type { Types } from '@cornerstonejs/core';
|
|
3
|
+
|
|
4
|
+
import type { vtkImageData } from '@kitware/vtk.js/Common/DataModel/ImageData';
|
|
5
|
+
import { vec3 } from 'gl-matrix';
|
|
6
|
+
import { pointInSphere } from './math/sphere';
|
|
7
|
+
import pointInShapeCallback, {
|
|
8
|
+
PointInShapeCallback,
|
|
9
|
+
} from './pointInShapeCallback';
|
|
10
|
+
import { BoundsIJK } from '../types';
|
|
11
|
+
import { getBoundingBoxAroundShape } from './boundingBox';
|
|
12
|
+
|
|
13
|
+
const { transformWorldToIndex } = csUtils;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Given an imageData, and the great circle top and bottom points of a sphere,
|
|
17
|
+
* this function will run the callback for each point of the imageData that is
|
|
18
|
+
* within the sphere defined by the great circle points. If the viewport
|
|
19
|
+
* is provided, region of interest will be an accurate approximation of the
|
|
20
|
+
* sphere (using viewport camera), and the resulting performance will be
|
|
21
|
+
* better.
|
|
22
|
+
*
|
|
23
|
+
* @privateRemarks great circle also known as orthodrome is the intersection of
|
|
24
|
+
* the sphere and the plane that passes through the center of the sphere
|
|
25
|
+
*
|
|
26
|
+
* @param imageData - The volume imageData
|
|
27
|
+
* @param circlePoints - bottom and top points of the great circle in world coordinates
|
|
28
|
+
* @param callback - A callback function that will be called for each point in the shape.
|
|
29
|
+
*/
|
|
30
|
+
export default function pointInSurroundingSphereCallback(
|
|
31
|
+
imageData: vtkImageData,
|
|
32
|
+
circlePoints: [Types.Point3, Types.Point3],
|
|
33
|
+
callback: PointInShapeCallback,
|
|
34
|
+
viewport?: Types.IVolumeViewport
|
|
35
|
+
): void {
|
|
36
|
+
// We can run the sphere equation to determine if a point is inside
|
|
37
|
+
// the sphere; however, since the imageData dimensions can be quite large, we
|
|
38
|
+
// can narrow down the search by estimating the bounds of the sphere in index
|
|
39
|
+
// space.
|
|
40
|
+
const { boundsIJK, centerWorld, radiusWorld } = _getBounds(
|
|
41
|
+
circlePoints,
|
|
42
|
+
imageData,
|
|
43
|
+
viewport
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const sphereObj = {
|
|
47
|
+
center: centerWorld,
|
|
48
|
+
radius: radiusWorld,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
pointInShapeCallback(
|
|
52
|
+
imageData,
|
|
53
|
+
(pointLPS) => pointInSphere(sphereObj, pointLPS),
|
|
54
|
+
callback,
|
|
55
|
+
boundsIJK
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function _getBounds(
|
|
60
|
+
circlePoints: [Types.Point3, Types.Point3],
|
|
61
|
+
imageData: vtkImageData,
|
|
62
|
+
viewport
|
|
63
|
+
): {
|
|
64
|
+
boundsIJK: BoundsIJK;
|
|
65
|
+
centerWorld: Types.Point3;
|
|
66
|
+
radiusWorld: number;
|
|
67
|
+
} {
|
|
68
|
+
const [bottom, top] = circlePoints;
|
|
69
|
+
|
|
70
|
+
// Sphere center in world
|
|
71
|
+
const centerWorld = vec3.fromValues(
|
|
72
|
+
(bottom[0] + top[0]) / 2,
|
|
73
|
+
(bottom[1] + top[1]) / 2,
|
|
74
|
+
(bottom[2] + top[2]) / 2
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// sphere radius in world
|
|
78
|
+
const radiusWorld = vec3.distance(bottom, top) / 2;
|
|
79
|
+
|
|
80
|
+
let boundsIJK;
|
|
81
|
+
|
|
82
|
+
if (!viewport) {
|
|
83
|
+
// If no viewport is provide (no camera), we can estimate the bounding box
|
|
84
|
+
// of the sphere in index space.
|
|
85
|
+
// This is done by calculating the maximum value for radius in the index
|
|
86
|
+
// space (since the radius is in world space, we need to convert it to index, and
|
|
87
|
+
// each dimensions can have a different scale factor). Therefore, by finding
|
|
88
|
+
// the minimum spacing value in the imageData, we can calculate the maximum
|
|
89
|
+
// radius in index space and use that to calculate the bounds of the sphere
|
|
90
|
+
// This will not be accurate, but it is a good first approximation.
|
|
91
|
+
// sphere center in index
|
|
92
|
+
const centerIJK = transformWorldToIndex(
|
|
93
|
+
imageData,
|
|
94
|
+
centerWorld as Types.Point3
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const spacings = imageData.getSpacing();
|
|
98
|
+
const minSpacing = Math.min(...spacings);
|
|
99
|
+
|
|
100
|
+
const maxRadiusIJK = Math.ceil(radiusWorld / minSpacing);
|
|
101
|
+
|
|
102
|
+
boundsIJK = [
|
|
103
|
+
[centerIJK[0] - maxRadiusIJK, centerIJK[0] + maxRadiusIJK],
|
|
104
|
+
[centerIJK[1] - maxRadiusIJK, centerIJK[1] + maxRadiusIJK],
|
|
105
|
+
[centerIJK[2] - maxRadiusIJK, centerIJK[2] + maxRadiusIJK],
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
boundsIJK,
|
|
110
|
+
centerWorld: centerWorld as Types.Point3,
|
|
111
|
+
radiusWorld,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
boundsIJK = _computeBoundsIJKWithCamera(
|
|
116
|
+
imageData,
|
|
117
|
+
viewport,
|
|
118
|
+
circlePoints,
|
|
119
|
+
centerWorld,
|
|
120
|
+
radiusWorld
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
boundsIJK,
|
|
125
|
+
centerWorld: centerWorld as Types.Point3,
|
|
126
|
+
radiusWorld,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function _computeBoundsIJKWithCamera(
|
|
131
|
+
imageData,
|
|
132
|
+
viewport,
|
|
133
|
+
circlePoints,
|
|
134
|
+
centerWorld,
|
|
135
|
+
radiusWorld
|
|
136
|
+
) {
|
|
137
|
+
const [bottom, top] = circlePoints;
|
|
138
|
+
|
|
139
|
+
const dimensions = imageData.getDimensions() as Types.Point3;
|
|
140
|
+
const camera = viewport.getCamera();
|
|
141
|
+
|
|
142
|
+
// Calculate viewRight from the camera, this will get used in order to
|
|
143
|
+
// calculate circles topLeft and bottomRight on different planes of intersection
|
|
144
|
+
// between sphere and viewPlane
|
|
145
|
+
const viewUp = vec3.fromValues(
|
|
146
|
+
camera.viewUp[0],
|
|
147
|
+
camera.viewUp[1],
|
|
148
|
+
camera.viewUp[2]
|
|
149
|
+
);
|
|
150
|
+
const viewPlaneNormal = vec3.fromValues(
|
|
151
|
+
camera.viewPlaneNormal[0],
|
|
152
|
+
camera.viewPlaneNormal[1],
|
|
153
|
+
camera.viewPlaneNormal[2]
|
|
154
|
+
);
|
|
155
|
+
const viewRight = vec3.create();
|
|
156
|
+
|
|
157
|
+
vec3.cross(viewRight, viewUp, viewPlaneNormal);
|
|
158
|
+
|
|
159
|
+
// we need to find the bounding box of the sphere in the image, e.g., the
|
|
160
|
+
// topLeftWorld and bottomRightWorld points of the bounding box.
|
|
161
|
+
// We go from the sphereCenter in the normal direction of amount radius, and
|
|
162
|
+
// we go left to find the topLeftWorld point of the bounding box. Next we go
|
|
163
|
+
// in the opposite direction and go right to find the bottomRightWorld point
|
|
164
|
+
// of the bounding box.
|
|
165
|
+
const topLeftWorld = vec3.create();
|
|
166
|
+
const bottomRightWorld = vec3.create();
|
|
167
|
+
|
|
168
|
+
vec3.scaleAndAdd(topLeftWorld, top, viewPlaneNormal, radiusWorld);
|
|
169
|
+
vec3.scaleAndAdd(bottomRightWorld, bottom, viewPlaneNormal, -radiusWorld);
|
|
170
|
+
|
|
171
|
+
// go in the direction of viewRight with the value of radius
|
|
172
|
+
vec3.scaleAndAdd(topLeftWorld, topLeftWorld, viewRight, -radiusWorld);
|
|
173
|
+
vec3.scaleAndAdd(bottomRightWorld, bottomRightWorld, viewRight, radiusWorld);
|
|
174
|
+
|
|
175
|
+
// convert the world coordinates to index coordinates
|
|
176
|
+
|
|
177
|
+
const sphereCornersIJK = [
|
|
178
|
+
<Types.Point3>transformWorldToIndex(imageData, <Types.Point3>topLeftWorld),
|
|
179
|
+
<Types.Point3>(
|
|
180
|
+
transformWorldToIndex(imageData, <Types.Point3>bottomRightWorld)
|
|
181
|
+
),
|
|
182
|
+
];
|
|
183
|
+
|
|
184
|
+
// get the bounding box of the sphere in the image
|
|
185
|
+
const boundsIJK = getBoundingBoxAroundShape(sphereCornersIJK, dimensions);
|
|
186
|
+
|
|
187
|
+
return boundsIJK;
|
|
188
|
+
}
|