@cornerstonejs/tools 1.61.7 → 1.63.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/index.d.ts +2 -2
- package/dist/cjs/index.js +2 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/tools/index.d.ts +2 -1
- package/dist/cjs/tools/index.js +4 -1
- package/dist/cjs/tools/index.js.map +1 -1
- package/dist/cjs/tools/segmentation/CircleROIStartEndThresholdTool.d.ts +63 -0
- package/dist/cjs/tools/segmentation/CircleROIStartEndThresholdTool.js +347 -0
- package/dist/cjs/tools/segmentation/CircleROIStartEndThresholdTool.js.map +1 -0
- package/dist/cjs/tools/segmentation/RectangleROIStartEndThresholdTool.d.ts +3 -0
- package/dist/cjs/tools/segmentation/RectangleROIStartEndThresholdTool.js +73 -0
- package/dist/cjs/tools/segmentation/RectangleROIStartEndThresholdTool.js.map +1 -1
- package/dist/cjs/types/ToolSpecificAnnotationTypes.d.ts +34 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/tools/index.js +2 -1
- package/dist/esm/tools/index.js.map +1 -1
- package/dist/esm/tools/segmentation/CircleROIStartEndThresholdTool.js +342 -0
- package/dist/esm/tools/segmentation/CircleROIStartEndThresholdTool.js.map +1 -0
- package/dist/esm/tools/segmentation/RectangleROIStartEndThresholdTool.js +75 -2
- package/dist/esm/tools/segmentation/RectangleROIStartEndThresholdTool.js.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/tools/index.d.ts +2 -1
- package/dist/types/tools/index.d.ts.map +1 -1
- package/dist/types/tools/segmentation/CircleROIStartEndThresholdTool.d.ts +64 -0
- package/dist/types/tools/segmentation/CircleROIStartEndThresholdTool.d.ts.map +1 -0
- package/dist/types/tools/segmentation/RectangleROIStartEndThresholdTool.d.ts +3 -0
- package/dist/types/tools/segmentation/RectangleROIStartEndThresholdTool.d.ts.map +1 -1
- package/dist/types/types/ToolSpecificAnnotationTypes.d.ts +34 -1
- package/dist/types/types/ToolSpecificAnnotationTypes.d.ts.map +1 -1
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +2 -0
- package/src/tools/index.ts +2 -0
- package/src/tools/segmentation/CircleROIStartEndThresholdTool.ts +677 -0
- package/src/tools/segmentation/RectangleROIStartEndThresholdTool.ts +134 -2
- package/src/types/ToolSpecificAnnotationTypes.ts +43 -8
|
@@ -8,7 +8,11 @@ import {
|
|
|
8
8
|
import type { Types } from '@cornerstonejs/core';
|
|
9
9
|
|
|
10
10
|
import { vec3 } from 'gl-matrix';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
addAnnotation,
|
|
13
|
+
getAnnotations,
|
|
14
|
+
removeAnnotation,
|
|
15
|
+
} from '../../stateManagement';
|
|
12
16
|
import { isAnnotationLocked } from '../../stateManagement/annotation/annotationLocking';
|
|
13
17
|
import { triggerAnnotationModified } from '../../stateManagement/annotation/helpers/state';
|
|
14
18
|
import {
|
|
@@ -18,8 +22,12 @@ import {
|
|
|
18
22
|
import { getViewportIdsWithToolToRender } from '../../utilities/viewportFilters';
|
|
19
23
|
import throttle from '../../utilities/throttle';
|
|
20
24
|
import { isAnnotationVisible } from '../../stateManagement/annotation/annotationVisibility';
|
|
21
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
hideElementCursor,
|
|
27
|
+
resetElementCursor,
|
|
28
|
+
} from '../../cursors/elementCursor';
|
|
22
29
|
import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds';
|
|
30
|
+
import { triggerAnnotationCompleted } from '../../stateManagement/annotation/helpers/state';
|
|
23
31
|
|
|
24
32
|
import {
|
|
25
33
|
PublicToolProps,
|
|
@@ -30,6 +38,7 @@ import {
|
|
|
30
38
|
import { RectangleROIStartEndThresholdAnnotation } from '../../types/ToolSpecificAnnotationTypes';
|
|
31
39
|
import RectangleROITool from '../annotation/RectangleROITool';
|
|
32
40
|
import { StyleSpecifier } from '../../types/AnnotationStyle';
|
|
41
|
+
import { pointInShapeCallback } from '../../utilities/';
|
|
33
42
|
|
|
34
43
|
const { transformWorldToIndex } = csUtils;
|
|
35
44
|
|
|
@@ -63,6 +72,7 @@ class RectangleROIStartEndThresholdTool extends RectangleROITool {
|
|
|
63
72
|
defaultToolProps: ToolProps = {
|
|
64
73
|
configuration: {
|
|
65
74
|
numSlicesToPropagate: 10,
|
|
75
|
+
computePointsInsideVolume: false,
|
|
66
76
|
},
|
|
67
77
|
}
|
|
68
78
|
) {
|
|
@@ -151,6 +161,7 @@ class RectangleROIStartEndThresholdTool extends RectangleROITool {
|
|
|
151
161
|
startSlice: startIndex,
|
|
152
162
|
endSlice: endIndex,
|
|
153
163
|
cachedStats: {
|
|
164
|
+
pointsInVolume: [],
|
|
154
165
|
projectionPoints: [],
|
|
155
166
|
projectionPointsImageIds: [referencedImageId],
|
|
156
167
|
},
|
|
@@ -203,6 +214,54 @@ class RectangleROIStartEndThresholdTool extends RectangleROITool {
|
|
|
203
214
|
return annotation;
|
|
204
215
|
};
|
|
205
216
|
|
|
217
|
+
_endCallback = (evt: EventTypes.InteractionEventType): void => {
|
|
218
|
+
const eventDetail = evt.detail;
|
|
219
|
+
const { element } = eventDetail;
|
|
220
|
+
|
|
221
|
+
const { annotation, viewportIdsToRender, newAnnotation, hasMoved } =
|
|
222
|
+
this.editData;
|
|
223
|
+
const { data } = annotation;
|
|
224
|
+
|
|
225
|
+
if (newAnnotation && !hasMoved) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
data.handles.activeHandleIndex = null;
|
|
230
|
+
|
|
231
|
+
this._deactivateModify(element);
|
|
232
|
+
this._deactivateDraw(element);
|
|
233
|
+
|
|
234
|
+
resetElementCursor(element);
|
|
235
|
+
|
|
236
|
+
const enabledElement = getEnabledElement(element);
|
|
237
|
+
|
|
238
|
+
this.editData = null;
|
|
239
|
+
this.isDrawing = false;
|
|
240
|
+
|
|
241
|
+
if (
|
|
242
|
+
this.isHandleOutsideImage &&
|
|
243
|
+
this.configuration.preventHandleOutsideImage
|
|
244
|
+
) {
|
|
245
|
+
removeAnnotation(annotation.annotationUID);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const targetId = this.getTargetId(enabledElement.viewport);
|
|
249
|
+
const imageVolume = cache.getVolume(targetId.split(/volumeId:|\?/)[1]);
|
|
250
|
+
|
|
251
|
+
if (this.configuration.calculatePointsInsideVolume) {
|
|
252
|
+
this._computePointsInsideVolume(annotation, imageVolume, enabledElement);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
triggerAnnotationRenderForViewportIds(
|
|
256
|
+
enabledElement.renderingEngine,
|
|
257
|
+
viewportIdsToRender
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
if (newAnnotation) {
|
|
261
|
+
triggerAnnotationCompleted(annotation);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
206
265
|
// Todo: make it work for planes other than acquisition planes
|
|
207
266
|
_computeProjectionPoints(
|
|
208
267
|
annotation: RectangleROIStartEndThresholdAnnotation,
|
|
@@ -261,6 +320,79 @@ class RectangleROIStartEndThresholdTool extends RectangleROITool {
|
|
|
261
320
|
data.cachedStats.projectionPointsImageIds = projectionPointsImageIds;
|
|
262
321
|
}
|
|
263
322
|
|
|
323
|
+
//This function return all the points inside the ROI for every slices between startSlice and endSlice
|
|
324
|
+
_computePointsInsideVolume(annotation, imageVolume, enabledElement) {
|
|
325
|
+
const { data } = annotation;
|
|
326
|
+
const projectionPoints = data.cachedStats.projectionPoints;
|
|
327
|
+
|
|
328
|
+
const pointsInsideVolume: Types.Point3[][] = [[]];
|
|
329
|
+
|
|
330
|
+
for (let i = 0; i < projectionPoints.length; i++) {
|
|
331
|
+
// If image does not exists for the targetId, skip. This can be due
|
|
332
|
+
// to various reasons such as if the target was a volumeViewport, and
|
|
333
|
+
// the volumeViewport has been decached in the meantime.
|
|
334
|
+
if (!imageVolume) {
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const projectionPoint = projectionPoints[i][0];
|
|
339
|
+
|
|
340
|
+
const worldPos1 = data.handles.points[0];
|
|
341
|
+
const worldPos2 = data.handles.points[3];
|
|
342
|
+
|
|
343
|
+
const { dimensions, imageData } = imageVolume;
|
|
344
|
+
|
|
345
|
+
const worldPos1Index = transformWorldToIndex(imageData, worldPos1);
|
|
346
|
+
//We only need to change the Z of our bounds so we are getting the Z from the current projection point
|
|
347
|
+
const worldProjectionPointIndex = transformWorldToIndex(
|
|
348
|
+
imageData,
|
|
349
|
+
projectionPoint
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
worldPos1Index[0] = Math.floor(worldPos1Index[0]);
|
|
353
|
+
worldPos1Index[1] = Math.floor(worldPos1Index[1]);
|
|
354
|
+
worldPos1Index[2] = Math.floor(worldProjectionPointIndex[2]);
|
|
355
|
+
|
|
356
|
+
const worldPos2Index = transformWorldToIndex(imageData, worldPos2);
|
|
357
|
+
|
|
358
|
+
worldPos2Index[0] = Math.floor(worldPos2Index[0]);
|
|
359
|
+
worldPos2Index[1] = Math.floor(worldPos2Index[1]);
|
|
360
|
+
worldPos2Index[2] = Math.floor(worldProjectionPointIndex[2]);
|
|
361
|
+
|
|
362
|
+
// Check if one of the indexes are inside the volume, this then gives us
|
|
363
|
+
// Some area to do stats over.
|
|
364
|
+
|
|
365
|
+
if (this._isInsideVolume(worldPos1Index, worldPos2Index, dimensions)) {
|
|
366
|
+
this.isHandleOutsideImage = false;
|
|
367
|
+
const iMin = Math.min(worldPos1Index[0], worldPos2Index[0]);
|
|
368
|
+
const iMax = Math.max(worldPos1Index[0], worldPos2Index[0]);
|
|
369
|
+
|
|
370
|
+
const jMin = Math.min(worldPos1Index[1], worldPos2Index[1]);
|
|
371
|
+
const jMax = Math.max(worldPos1Index[1], worldPos2Index[1]);
|
|
372
|
+
|
|
373
|
+
const kMin = Math.min(worldPos1Index[2], worldPos2Index[2]);
|
|
374
|
+
const kMax = Math.max(worldPos1Index[2], worldPos2Index[2]);
|
|
375
|
+
|
|
376
|
+
const boundsIJK = [
|
|
377
|
+
[iMin, iMax],
|
|
378
|
+
[jMin, jMax],
|
|
379
|
+
[kMin, kMax],
|
|
380
|
+
] as [Types.Point2, Types.Point2, Types.Point2];
|
|
381
|
+
|
|
382
|
+
const pointsInShape = pointInShapeCallback(
|
|
383
|
+
imageData,
|
|
384
|
+
() => true,
|
|
385
|
+
null,
|
|
386
|
+
boundsIJK
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
//@ts-ignore
|
|
390
|
+
pointsInsideVolume.push(pointsInShape);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
data.cachedStats.pointsInVolume = pointsInsideVolume;
|
|
394
|
+
}
|
|
395
|
+
|
|
264
396
|
_calculateCachedStatsTool(annotation, enabledElement) {
|
|
265
397
|
const data = annotation.data;
|
|
266
398
|
const { element, viewport } = enabledElement;
|
|
@@ -35,6 +35,7 @@ export interface RectangleROIAnnotation extends Annotation {
|
|
|
35
35
|
cachedStats?:
|
|
36
36
|
| ROICachedStats
|
|
37
37
|
| {
|
|
38
|
+
pointsInVolume?: Types.Point3[];
|
|
38
39
|
projectionPoints?: Types.Point3[];
|
|
39
40
|
projectionPointsImageIds?: string[];
|
|
40
41
|
};
|
|
@@ -110,13 +111,18 @@ export interface CircleROIAnnotation extends Annotation {
|
|
|
110
111
|
};
|
|
111
112
|
};
|
|
112
113
|
label: string;
|
|
113
|
-
cachedStats?:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
114
|
+
cachedStats?:
|
|
115
|
+
| (ROICachedStats & {
|
|
116
|
+
[targetId: string]: {
|
|
117
|
+
radius: number;
|
|
118
|
+
radiusUnit: string;
|
|
119
|
+
perimeter: number;
|
|
120
|
+
};
|
|
121
|
+
})
|
|
122
|
+
| {
|
|
123
|
+
pointsInVolume: Types.Point3[];
|
|
124
|
+
projectionPoints: Types.Point3[][];
|
|
125
|
+
};
|
|
120
126
|
};
|
|
121
127
|
}
|
|
122
128
|
|
|
@@ -236,6 +242,7 @@ export interface RectangleROIStartEndThresholdAnnotation extends Annotation {
|
|
|
236
242
|
startSlice: number;
|
|
237
243
|
endSlice: number;
|
|
238
244
|
cachedStats: {
|
|
245
|
+
pointsInVolume: Types.Point3[];
|
|
239
246
|
projectionPoints: Types.Point3[][]; // first slice p1, p2, p3, p4; second slice p1, p2, p3, p4 ...
|
|
240
247
|
projectionPointsImageIds: string[];
|
|
241
248
|
};
|
|
@@ -246,6 +253,35 @@ export interface RectangleROIStartEndThresholdAnnotation extends Annotation {
|
|
|
246
253
|
};
|
|
247
254
|
}
|
|
248
255
|
|
|
256
|
+
export interface CircleROIStartEndThresholdAnnotation extends Annotation {
|
|
257
|
+
metadata: {
|
|
258
|
+
cameraPosition?: Types.Point3;
|
|
259
|
+
cameraFocalPoint?: Types.Point3;
|
|
260
|
+
viewPlaneNormal?: Types.Point3;
|
|
261
|
+
viewUp?: Types.Point3;
|
|
262
|
+
annotationUID?: string;
|
|
263
|
+
FrameOfReferenceUID: string;
|
|
264
|
+
referencedImageId?: string;
|
|
265
|
+
toolName: string;
|
|
266
|
+
enabledElement: any; // Todo: how to remove this from the annotation??
|
|
267
|
+
volumeId: string;
|
|
268
|
+
spacingInNormal: number;
|
|
269
|
+
};
|
|
270
|
+
data: {
|
|
271
|
+
label: string;
|
|
272
|
+
startSlice: number;
|
|
273
|
+
endSlice: number;
|
|
274
|
+
cachedStats?: {
|
|
275
|
+
pointsInVolume: Types.Point3[];
|
|
276
|
+
projectionPoints: Types.Point3[][];
|
|
277
|
+
};
|
|
278
|
+
handles: {
|
|
279
|
+
points: [Types.Point3, Types.Point3]; // [center, end]
|
|
280
|
+
activeHandleIndex: number | null;
|
|
281
|
+
};
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
249
285
|
export type PlanarFreehandROIAnnotation = ContourAnnotation & {
|
|
250
286
|
data: {
|
|
251
287
|
label?: string;
|
|
@@ -256,7 +292,6 @@ export type PlanarFreehandROIAnnotation = ContourAnnotation & {
|
|
|
256
292
|
cachedStats?: ROICachedStats;
|
|
257
293
|
};
|
|
258
294
|
};
|
|
259
|
-
|
|
260
295
|
export type PlanarFreehandContourSegmentationAnnotation =
|
|
261
296
|
PlanarFreehandROIAnnotation & ContourSegmentationAnnotationData;
|
|
262
297
|
|