@cornerstonejs/tools 2.0.0-beta.24 → 2.0.0-beta.26
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/esm/enums/SegmentationRepresentations.d.ts +3 -3
- package/dist/esm/enums/SegmentationRepresentations.js +3 -3
- package/dist/esm/eventDispatchers/mouseEventHandlers/mouseMove.js +4 -0
- package/dist/esm/stateManagement/index.d.ts +3 -2
- package/dist/esm/stateManagement/index.js +2 -2
- package/dist/esm/stateManagement/segmentation/SegmentationStateManager.d.ts +6 -6
- package/dist/esm/stateManagement/segmentation/SegmentationStateManager.js +14 -14
- package/dist/esm/stateManagement/segmentation/addSegmentationRepresentations.d.ts +8 -3
- package/dist/esm/stateManagement/segmentation/addSegmentationRepresentations.js +14 -4
- package/dist/esm/stateManagement/segmentation/config/segmentationConfig.d.ts +8 -7
- package/dist/esm/stateManagement/segmentation/convertVolumeToStackSegmentation.js +1 -1
- package/dist/esm/stateManagement/segmentation/getGlobalConfig.d.ts +2 -2
- package/dist/esm/stateManagement/segmentation/getPerSegmentConfig.d.ts +2 -2
- package/dist/esm/stateManagement/segmentation/helpers/updateStackSegmentationState.js +5 -5
- package/dist/esm/stateManagement/segmentation/index.d.ts +2 -2
- package/dist/esm/stateManagement/segmentation/index.js +2 -2
- package/dist/esm/stateManagement/segmentation/internalAddSegmentationRepresentation.d.ts +3 -0
- package/dist/esm/stateManagement/segmentation/{addSegmentationRepresentation.js → internalAddSegmentationRepresentation.js} +4 -3
- package/dist/esm/stateManagement/segmentation/polySeg/Contour/contourComputationStrategies.js +4 -4
- package/dist/esm/stateManagement/segmentation/polySeg/Labelmap/labelmapComputationStrategies.js +4 -4
- package/dist/esm/stateManagement/segmentation/polySeg/Surface/createAndCacheSurfacesFromRaw.js +1 -1
- package/dist/esm/stateManagement/segmentation/polySeg/Surface/surfaceComputationStrategies.js +6 -6
- package/dist/esm/stateManagement/segmentation/polySeg/Surface/updateSurfaceData.js +2 -2
- package/dist/esm/stateManagement/segmentation/segmentationState.d.ts +1 -2
- package/dist/esm/stateManagement/segmentation/segmentationState.js +1 -2
- package/dist/esm/stateManagement/segmentation/setGlobalConfig.d.ts +2 -2
- package/dist/esm/stateManagement/segmentation/setPerSegmentConfig.d.ts +2 -2
- package/dist/esm/store/ToolGroupManager/ToolGroup.d.ts +1 -1
- package/dist/esm/tools/AdvancedMagnifyTool.js +2 -2
- package/dist/esm/tools/ScaleOverlayTool.js +23 -20
- package/dist/esm/tools/annotation/LivewireContourTool.d.ts +5 -0
- package/dist/esm/tools/annotation/LivewireContourTool.js +156 -8
- package/dist/esm/tools/base/BaseTool.d.ts +1 -13
- package/dist/esm/tools/base/ContourSegmentationBaseTool.js +1 -1
- package/dist/esm/tools/displayTools/Contour/contourHandler/handleContourSegmentation.js +4 -4
- package/dist/esm/tools/displayTools/Contour/contourHandler/utils.js +1 -1
- package/dist/esm/tools/displayTools/Contour/removeContourFromElement.js +1 -1
- package/dist/esm/tools/segmentation/CircleROIStartEndThresholdTool.js +23 -15
- package/dist/esm/tools/segmentation/RectangleROIStartEndThresholdTool.js +12 -0
- package/dist/esm/types/EventTypes.d.ts +1 -2
- package/dist/esm/types/IBaseTool.d.ts +2 -0
- package/dist/esm/types/IBaseTool.js +1 -0
- package/dist/esm/types/IToolGroup.d.ts +3 -63
- package/dist/esm/types/SegmentationStateTypes.d.ts +22 -22
- package/dist/esm/types/index.d.ts +3 -2
- package/dist/esm/utilities/contourSegmentation/addContourSegmentationAnnotation.js +3 -3
- package/dist/esm/utilities/contourSegmentation/removeContourSegmentationAnnotation.js +1 -1
- package/dist/esm/utilities/segmentation/getHoveredContourSegmentationAnnotation.js +1 -1
- package/dist/esm/utilities/segmentation/getSegmentIndexAtLabelmapBorder.js +1 -1
- package/dist/esm/utilities/segmentation/getSegmentIndexAtWorldPoint.js +2 -2
- package/dist/esm/utilities/segmentation/getUniqueSegmentIndices.js +2 -2
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +16 -7
- package/dist/esm/stateManagement/segmentation/addSegmentationRepresentation.d.ts +0 -3
|
@@ -44,27 +44,30 @@ class ScaleOverlayTool extends AnnotationDisplayTool {
|
|
|
44
44
|
if (annotations.length) {
|
|
45
45
|
annotation = annotations.filter((thisAnnotation) => thisAnnotation.data.viewportId == viewport.id)[0];
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
handles: {
|
|
58
|
-
points: viewportCanvasCornersInWorld,
|
|
47
|
+
enabledElements.forEach((element) => {
|
|
48
|
+
const { viewport } = element;
|
|
49
|
+
if (!viewportsWithAnnotations.includes(viewport.id)) {
|
|
50
|
+
const newAnnotation = {
|
|
51
|
+
metadata: {
|
|
52
|
+
toolName: this.getToolName(),
|
|
53
|
+
viewPlaneNormal: [...viewPlaneNormal],
|
|
54
|
+
viewUp: [...viewUp],
|
|
55
|
+
FrameOfReferenceUID,
|
|
56
|
+
referencedImageId: null,
|
|
59
57
|
},
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
58
|
+
data: {
|
|
59
|
+
handles: {
|
|
60
|
+
points: csUtils.getViewportImageCornersInWorld(viewport),
|
|
61
|
+
},
|
|
62
|
+
viewportId: viewport.id,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
viewportsWithAnnotations.push(viewport.id);
|
|
66
|
+
addAnnotation(newAnnotation, viewport.element);
|
|
67
|
+
annotation = newAnnotation;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
if (this.editData.annotation &&
|
|
68
71
|
this.editData.annotation.data.viewportId == viewport.id) {
|
|
69
72
|
this.editData.annotation.data.handles.points =
|
|
70
73
|
viewportCanvasCornersInWorld;
|
|
@@ -10,10 +10,12 @@ declare class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
10
10
|
static toolName: string;
|
|
11
11
|
protected scissors: LivewireScissors;
|
|
12
12
|
protected scissorsNext: LivewireScissors;
|
|
13
|
+
_throttledCalculateCachedStats: Function;
|
|
13
14
|
editData: {
|
|
14
15
|
annotation: LivewireContourAnnotation;
|
|
15
16
|
viewportIdsToRender: Array<string>;
|
|
16
17
|
handleIndex?: number;
|
|
18
|
+
movingTextBox?: boolean;
|
|
17
19
|
newAnnotation?: boolean;
|
|
18
20
|
hasMoved?: boolean;
|
|
19
21
|
lastCanvasPoint?: Types.Point2;
|
|
@@ -57,6 +59,9 @@ declare class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
57
59
|
annotationStyle: AnnotationStyle;
|
|
58
60
|
svgDrawingHelper: SVGDrawingHelper;
|
|
59
61
|
}): boolean;
|
|
62
|
+
private _calculateCachedStats;
|
|
63
|
+
private _renderStats;
|
|
64
|
+
triggerAnnotationModified: (annotation: LivewireContourAnnotation, enabledElement: Types.IEnabledElement, changeType?: ChangeTypes) => void;
|
|
60
65
|
protected updateAnnotation(livewirePath: LivewirePath): void;
|
|
61
66
|
}
|
|
62
67
|
export default LivewireContourTool;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { vec3 } from 'gl-matrix';
|
|
2
|
-
import { getEnabledElement, utilities as csUtils, VolumeViewport, } from '@cornerstonejs/core';
|
|
2
|
+
import { getEnabledElement, utilities as csUtils, VolumeViewport, utilities, triggerEvent, eventTarget, } from '@cornerstonejs/core';
|
|
3
3
|
import { removeAnnotation } from '../../stateManagement/annotation/annotationState';
|
|
4
|
-
import { drawHandles as drawHandlesSvg } from '../../drawingSvg';
|
|
4
|
+
import { drawHandles as drawHandlesSvg, drawLinkedTextBox as drawLinkedTextBoxSvg, } from '../../drawingSvg';
|
|
5
5
|
import { state } from '../../store/state';
|
|
6
6
|
import { Events, KeyboardBindings, ChangeTypes } from '../../enums';
|
|
7
7
|
import { resetElementCursor } from '../../cursors/elementCursor';
|
|
@@ -15,11 +15,15 @@ import { LivewireScissors } from '../../utilities/livewire/LivewireScissors';
|
|
|
15
15
|
import { LivewirePath } from '../../utilities/livewire/LiveWirePath';
|
|
16
16
|
import { getViewportIdsWithToolToRender } from '../../utilities/viewportFilters';
|
|
17
17
|
import ContourSegmentationBaseTool from '../base/ContourSegmentationBaseTool';
|
|
18
|
+
import { getTextBoxCoordsCanvas } from '../../utilities/drawing';
|
|
19
|
+
import { getCalibratedLengthUnitsAndScale, throttle } from '../../utilities';
|
|
18
20
|
const CLICK_CLOSE_CURVE_SQR_DIST = 10 ** 2;
|
|
19
21
|
class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
20
22
|
constructor(toolProps = {}, defaultToolProps = {
|
|
21
23
|
supportedInteractionTypes: ['Mouse', 'Touch'],
|
|
22
24
|
configuration: {
|
|
25
|
+
getTextLines: defaultGetTextLines,
|
|
26
|
+
calculateStats: true,
|
|
23
27
|
preventHandleOutsideImage: false,
|
|
24
28
|
contourHoleAdditionModifierKey: KeyboardBindings.Shift,
|
|
25
29
|
snapHandleNearby: 2,
|
|
@@ -70,6 +74,7 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
70
74
|
this.editData = {
|
|
71
75
|
annotation,
|
|
72
76
|
viewportIdsToRender,
|
|
77
|
+
movingTextBox: false,
|
|
73
78
|
};
|
|
74
79
|
const enabledElement = getEnabledElement(element);
|
|
75
80
|
const { renderingEngine } = enabledElement;
|
|
@@ -82,13 +87,21 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
82
87
|
const { element } = eventDetail;
|
|
83
88
|
const { data } = annotation;
|
|
84
89
|
annotation.highlighted = true;
|
|
85
|
-
|
|
86
|
-
|
|
90
|
+
let movingTextBox = false;
|
|
91
|
+
let handleIndex;
|
|
92
|
+
if (handle.worldPosition) {
|
|
93
|
+
movingTextBox = true;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
const { points } = data.handles;
|
|
97
|
+
handleIndex = points.findIndex((p) => p === handle);
|
|
98
|
+
}
|
|
87
99
|
const viewportIdsToRender = getViewportIdsWithToolToRender(element, this.getToolName());
|
|
88
100
|
this.editData = {
|
|
89
101
|
annotation,
|
|
90
102
|
viewportIdsToRender,
|
|
91
103
|
handleIndex,
|
|
104
|
+
movingTextBox,
|
|
92
105
|
};
|
|
93
106
|
this._activateModify(element);
|
|
94
107
|
const enabledElement = getEnabledElement(element);
|
|
@@ -215,15 +228,27 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
215
228
|
this.isDrawing = true;
|
|
216
229
|
const eventDetail = evt.detail;
|
|
217
230
|
const { element } = eventDetail;
|
|
218
|
-
const { annotation, viewportIdsToRender, handleIndex } = this.editData;
|
|
219
|
-
|
|
220
|
-
|
|
231
|
+
const { annotation, viewportIdsToRender, handleIndex, movingTextBox } = this.editData;
|
|
232
|
+
const { data } = annotation;
|
|
233
|
+
if (movingTextBox) {
|
|
234
|
+
const { deltaPoints } = eventDetail;
|
|
235
|
+
const worldPosDelta = deltaPoints.world;
|
|
236
|
+
const { textBox } = data.handles;
|
|
237
|
+
const { worldPosition } = textBox;
|
|
238
|
+
worldPosition[0] += worldPosDelta[0];
|
|
239
|
+
worldPosition[1] += worldPosDelta[1];
|
|
240
|
+
worldPosition[2] += worldPosDelta[2];
|
|
241
|
+
textBox.hasMoved = true;
|
|
242
|
+
}
|
|
243
|
+
else if (handleIndex === undefined) {
|
|
244
|
+
console.warn('Drag annotation not implemented');
|
|
221
245
|
}
|
|
222
246
|
else {
|
|
223
247
|
const { currentPoints } = eventDetail;
|
|
224
248
|
const worldPos = currentPoints.world;
|
|
225
249
|
this.editHandle(worldPos, element, annotation, handleIndex);
|
|
226
250
|
}
|
|
251
|
+
this.editData.hasMoved = true;
|
|
227
252
|
const enabledElement = getEnabledElement(element);
|
|
228
253
|
const { renderingEngine } = enabledElement;
|
|
229
254
|
triggerAnnotationRenderForViewportIds(viewportIdsToRender);
|
|
@@ -279,6 +304,104 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
279
304
|
element.removeEventListener(Events.MOUSE_DOUBLE_CLICK, this._mouseDownCallback);
|
|
280
305
|
element.removeEventListener(Events.TOUCH_TAP, this._mouseDownCallback);
|
|
281
306
|
};
|
|
307
|
+
this._calculateCachedStats = (annotation, element) => {
|
|
308
|
+
if (!this.configuration.calculateStats) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const data = annotation.data;
|
|
312
|
+
if (!data.contour.closed) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
const enabledElement = getEnabledElement(element);
|
|
316
|
+
const { viewport, renderingEngine } = enabledElement;
|
|
317
|
+
const { cachedStats } = data;
|
|
318
|
+
const { polyline: points } = data.contour;
|
|
319
|
+
const targetIds = Object.keys(cachedStats);
|
|
320
|
+
for (let i = 0; i < targetIds.length; i++) {
|
|
321
|
+
const targetId = targetIds[i];
|
|
322
|
+
const image = this.getTargetIdImage(targetId, renderingEngine);
|
|
323
|
+
if (!image) {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
const { metadata } = image;
|
|
327
|
+
const canvasCoordinates = points.map((p) => viewport.worldToCanvas(p));
|
|
328
|
+
const canvasPoint = canvasCoordinates[0];
|
|
329
|
+
const originalWorldPoint = viewport.canvasToWorld(canvasPoint);
|
|
330
|
+
const deltaXPoint = viewport.canvasToWorld([
|
|
331
|
+
canvasPoint[0] + 1,
|
|
332
|
+
canvasPoint[1],
|
|
333
|
+
]);
|
|
334
|
+
const deltaYPoint = viewport.canvasToWorld([
|
|
335
|
+
canvasPoint[0],
|
|
336
|
+
canvasPoint[1] + 1,
|
|
337
|
+
]);
|
|
338
|
+
const deltaInX = vec3.distance(originalWorldPoint, deltaXPoint);
|
|
339
|
+
const deltaInY = vec3.distance(originalWorldPoint, deltaYPoint);
|
|
340
|
+
const { imageData } = image;
|
|
341
|
+
const { scale, areaUnit } = getCalibratedLengthUnitsAndScale(image, () => {
|
|
342
|
+
const { maxX: canvasMaxX, maxY: canvasMaxY, minX: canvasMinX, minY: canvasMinY, } = math.polyline.getAABB(canvasCoordinates);
|
|
343
|
+
const topLeftBBWorld = viewport.canvasToWorld([
|
|
344
|
+
canvasMinX,
|
|
345
|
+
canvasMinY,
|
|
346
|
+
]);
|
|
347
|
+
const topLeftBBIndex = utilities.transformWorldToIndex(imageData, topLeftBBWorld);
|
|
348
|
+
const bottomRightBBWorld = viewport.canvasToWorld([
|
|
349
|
+
canvasMaxX,
|
|
350
|
+
canvasMaxY,
|
|
351
|
+
]);
|
|
352
|
+
const bottomRightBBIndex = utilities.transformWorldToIndex(imageData, bottomRightBBWorld);
|
|
353
|
+
return [topLeftBBIndex, bottomRightBBIndex];
|
|
354
|
+
});
|
|
355
|
+
let area = math.polyline.getArea(canvasCoordinates) / scale / scale;
|
|
356
|
+
area *= deltaInX * deltaInY;
|
|
357
|
+
cachedStats[targetId] = {
|
|
358
|
+
Modality: metadata.Modality,
|
|
359
|
+
area,
|
|
360
|
+
areaUnit: areaUnit,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
this.triggerAnnotationModified(annotation, enabledElement, ChangeTypes.StatsUpdated);
|
|
364
|
+
return cachedStats;
|
|
365
|
+
};
|
|
366
|
+
this._renderStats = (annotation, viewport, svgDrawingHelper, textboxStyle) => {
|
|
367
|
+
const data = annotation.data;
|
|
368
|
+
const targetId = this.getTargetId(viewport);
|
|
369
|
+
if (!data.contour.closed || !textboxStyle.visibility) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const textLines = this.configuration.getTextLines(data, targetId);
|
|
373
|
+
if (!textLines || textLines.length === 0) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
const canvasCoordinates = data.handles.points.map((p) => viewport.worldToCanvas(p));
|
|
377
|
+
if (!data.handles.textBox.hasMoved) {
|
|
378
|
+
const canvasTextBoxCoords = getTextBoxCoordsCanvas(canvasCoordinates);
|
|
379
|
+
data.handles.textBox.worldPosition =
|
|
380
|
+
viewport.canvasToWorld(canvasTextBoxCoords);
|
|
381
|
+
}
|
|
382
|
+
const textBoxPosition = viewport.worldToCanvas(data.handles.textBox.worldPosition);
|
|
383
|
+
const textBoxUID = 'textBox';
|
|
384
|
+
const boundingBox = drawLinkedTextBoxSvg(svgDrawingHelper, annotation.annotationUID ?? '', textBoxUID, textLines, textBoxPosition, canvasCoordinates, {}, textboxStyle);
|
|
385
|
+
const { x: left, y: top, width, height } = boundingBox;
|
|
386
|
+
data.handles.textBox.worldBoundingBox = {
|
|
387
|
+
topLeft: viewport.canvasToWorld([left, top]),
|
|
388
|
+
topRight: viewport.canvasToWorld([left + width, top]),
|
|
389
|
+
bottomLeft: viewport.canvasToWorld([left, top + height]),
|
|
390
|
+
bottomRight: viewport.canvasToWorld([left + width, top + height]),
|
|
391
|
+
};
|
|
392
|
+
};
|
|
393
|
+
this.triggerAnnotationModified = (annotation, enabledElement, changeType = ChangeTypes.StatsUpdated) => {
|
|
394
|
+
const { viewportId, renderingEngineId } = enabledElement;
|
|
395
|
+
const eventType = Events.ANNOTATION_MODIFIED;
|
|
396
|
+
const eventDetail = {
|
|
397
|
+
annotation,
|
|
398
|
+
viewportId,
|
|
399
|
+
renderingEngineId,
|
|
400
|
+
changeType,
|
|
401
|
+
};
|
|
402
|
+
triggerEvent(eventTarget, eventType, eventDetail);
|
|
403
|
+
};
|
|
404
|
+
this._throttledCalculateCachedStats = throttle(this._calculateCachedStats, 100, { trailing: true });
|
|
282
405
|
}
|
|
283
406
|
setupBaseEditData(worldPos, element, annotation, nextPos, contourHoleProcessingEnabled) {
|
|
284
407
|
const enabledElement = getEnabledElement(element);
|
|
@@ -432,6 +555,7 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
432
555
|
editData.currentPath = currentPath;
|
|
433
556
|
annotation.invalidated = true;
|
|
434
557
|
editData.hasMoved = true;
|
|
558
|
+
editData.closed = true;
|
|
435
559
|
}
|
|
436
560
|
renderAnnotation(enabledElement, svgDrawingHelper) {
|
|
437
561
|
this.updateAnnotation(this.editData?.currentPath);
|
|
@@ -459,8 +583,9 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
459
583
|
this._endCallback(evt, true);
|
|
460
584
|
}
|
|
461
585
|
renderAnnotationInstance(renderContext) {
|
|
462
|
-
const { annotation, enabledElement, svgDrawingHelper, annotationStyle } = renderContext;
|
|
586
|
+
const { annotation, enabledElement, svgDrawingHelper, annotationStyle, targetId, } = renderContext;
|
|
463
587
|
const { viewport } = enabledElement;
|
|
588
|
+
const { element } = viewport;
|
|
464
589
|
const { worldToCanvas } = viewport;
|
|
465
590
|
const { annotationUID, data, highlighted } = annotation;
|
|
466
591
|
const { handles } = data;
|
|
@@ -478,6 +603,19 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
478
603
|
});
|
|
479
604
|
}
|
|
480
605
|
super.renderAnnotationInstance(renderContext);
|
|
606
|
+
if (!data.cachedStats[targetId] ||
|
|
607
|
+
data.cachedStats[targetId].areaUnit == null) {
|
|
608
|
+
data.cachedStats[targetId] = {
|
|
609
|
+
Modality: null,
|
|
610
|
+
area: null,
|
|
611
|
+
areaUnit: null,
|
|
612
|
+
};
|
|
613
|
+
this._calculateCachedStats(annotation, element);
|
|
614
|
+
}
|
|
615
|
+
else if (annotation.invalidated) {
|
|
616
|
+
this._throttledCalculateCachedStats(annotation, element);
|
|
617
|
+
}
|
|
618
|
+
this._renderStats(annotation, viewport, svgDrawingHelper, annotationStyle.textbox);
|
|
481
619
|
return true;
|
|
482
620
|
}
|
|
483
621
|
updateAnnotation(livewirePath) {
|
|
@@ -502,3 +640,13 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
502
640
|
}
|
|
503
641
|
LivewireContourTool.toolName = 'LivewireContour';
|
|
504
642
|
export default LivewireContourTool;
|
|
643
|
+
function defaultGetTextLines(data, targetId) {
|
|
644
|
+
const cachedVolumeStats = data.cachedStats[targetId];
|
|
645
|
+
const { area, areaUnit } = cachedVolumeStats;
|
|
646
|
+
const textLines = [];
|
|
647
|
+
if (area) {
|
|
648
|
+
const areaLine = `Area: ${csUtils.roundNumber(area)} ${areaUnit}`;
|
|
649
|
+
textLines.push(areaLine);
|
|
650
|
+
}
|
|
651
|
+
return textLines;
|
|
652
|
+
}
|
|
@@ -2,19 +2,7 @@ import type { Types } from '@cornerstonejs/core';
|
|
|
2
2
|
import ToolModes from '../../enums/ToolModes';
|
|
3
3
|
import type StrategyCallbacks from '../../enums/StrategyCallbacks';
|
|
4
4
|
import type { InteractionTypes, ToolProps, PublicToolProps } from '../../types';
|
|
5
|
-
|
|
6
|
-
toolGroupId: string;
|
|
7
|
-
supportedInteractionTypes: InteractionTypes[];
|
|
8
|
-
mode: ToolModes;
|
|
9
|
-
configuration: {
|
|
10
|
-
preventHandleOutsideImage?: boolean;
|
|
11
|
-
strategies?: Record<string, unknown>;
|
|
12
|
-
defaultStrategy?: string;
|
|
13
|
-
activeStrategy?: string;
|
|
14
|
-
strategyOptions?: Record<string, unknown>;
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
declare abstract class BaseTool implements IBaseTool {
|
|
5
|
+
declare abstract class BaseTool {
|
|
18
6
|
static toolName: any;
|
|
19
7
|
supportedInteractionTypes: InteractionTypes[];
|
|
20
8
|
configuration: Record<string, any>;
|
|
@@ -128,7 +128,7 @@ class ContourSegmentationBaseTool extends ContourBaseTool {
|
|
|
128
128
|
const activeSegRep = getActiveSegmentationRepresentation(viewportId);
|
|
129
129
|
const isActive = activeSegRep.segmentationRepresentationUID ===
|
|
130
130
|
segmentationRepresentationUID;
|
|
131
|
-
const mergedConfig = Object.assign({}, globalConfig?.representations?.
|
|
131
|
+
const mergedConfig = Object.assign({}, globalConfig?.representations?.Contour ?? {}, segmentationRepresentationConfig ?? {}, segmentConfig ?? {});
|
|
132
132
|
let lineWidth = 1;
|
|
133
133
|
let lineDash = undefined;
|
|
134
134
|
let lineOpacity = 1;
|
|
@@ -15,8 +15,8 @@ function handleContourSegmentation(viewport, geometryIds, annotationUIDsMap, con
|
|
|
15
15
|
}
|
|
16
16
|
function updateContourSets(viewport, geometryIds, contourRepresentation) {
|
|
17
17
|
const { segmentationRepresentationUID, config } = contourRepresentation;
|
|
18
|
-
const baseConfig = config?.allSegments
|
|
19
|
-
const globalContourConfig = getGlobalConfig().representations.
|
|
18
|
+
const baseConfig = config?.allSegments;
|
|
19
|
+
const globalContourConfig = getGlobalConfig().representations.Contour;
|
|
20
20
|
const newContourConfig = utilities.deepMerge(globalContourConfig, baseConfig);
|
|
21
21
|
const cachedConfig = getConfigCache(segmentationRepresentationUID);
|
|
22
22
|
const newOutlineWithActive = newContourConfig.outlineWidthActive;
|
|
@@ -112,8 +112,8 @@ function addContourSetsToElement(viewport, geometryIds, contourRepresentation) {
|
|
|
112
112
|
segmentSpecificMap.set(segmentIndex, segmentSpecificConfig);
|
|
113
113
|
}
|
|
114
114
|
});
|
|
115
|
-
const baseConfig = contourRepresentation.config?.allSegments
|
|
116
|
-
const globalContourConfig = getGlobalConfig().representations.
|
|
115
|
+
const baseConfig = contourRepresentation.config?.allSegments;
|
|
116
|
+
const globalContourConfig = getGlobalConfig().representations.Contour;
|
|
117
117
|
const newContourConfig = utilities.deepMerge(globalContourConfig, baseConfig);
|
|
118
118
|
const outlineWidthActive = newContourConfig.outlineWidthActive;
|
|
119
119
|
const segmentsHidden = getHiddenSegmentIndices(viewport.id, segmentationRepresentationUID);
|
|
@@ -7,7 +7,7 @@ export function validateGeometry(geometry) {
|
|
|
7
7
|
throw new Error(`No contours found for geometryId ${geometry.id}`);
|
|
8
8
|
}
|
|
9
9
|
const geometryId = geometry.id;
|
|
10
|
-
if (geometry.type !== Enums.GeometryType.
|
|
10
|
+
if (geometry.type !== Enums.GeometryType.Contour) {
|
|
11
11
|
throw new Error(`Geometry type ${geometry.type} not supported for rendering.`);
|
|
12
12
|
}
|
|
13
13
|
if (!geometry.data) {
|
|
@@ -4,7 +4,7 @@ function removeContourFromElement(element, segmentationRepresentationUID, remove
|
|
|
4
4
|
const segmentationRepresentation = getSegmentationRepresentation(segmentationRepresentationUID);
|
|
5
5
|
const { segmentationId } = segmentationRepresentation;
|
|
6
6
|
const segmentation = getSegmentation(segmentationId);
|
|
7
|
-
const { annotationUIDsMap } = segmentation.representationData.
|
|
7
|
+
const { annotationUIDsMap } = segmentation.representationData.Contour;
|
|
8
8
|
annotationUIDsMap.forEach((annotationSet) => {
|
|
9
9
|
annotationSet.forEach((annotationUID) => {
|
|
10
10
|
removeAnnotation(annotationUID);
|
|
@@ -171,30 +171,35 @@ class CircleROIStartEndThresholdTool extends CircleROITool {
|
|
|
171
171
|
const canvasCorners = getCanvasCircleCorners(canvasCoordinates);
|
|
172
172
|
const focalPoint = viewport.getCamera().focalPoint;
|
|
173
173
|
const viewplaneNormal = viewport.getCamera().viewPlaneNormal;
|
|
174
|
-
let
|
|
175
|
-
let
|
|
174
|
+
let tempStartCoordinate = startCoordinate;
|
|
175
|
+
let tempEndCoordinate = endCoordinate;
|
|
176
176
|
if (Array.isArray(startCoordinate)) {
|
|
177
|
-
|
|
177
|
+
tempStartCoordinate = this._getCoordinateForViewplaneNormal(tempStartCoordinate, viewplaneNormal);
|
|
178
|
+
data.startCoordinate = tempStartCoordinate;
|
|
178
179
|
}
|
|
179
180
|
if (Array.isArray(endCoordinate)) {
|
|
180
|
-
|
|
181
|
+
tempEndCoordinate = this._getCoordinateForViewplaneNormal(tempEndCoordinate, viewplaneNormal);
|
|
182
|
+
data.endCoordinate = tempEndCoordinate;
|
|
181
183
|
}
|
|
182
|
-
const
|
|
183
|
-
const
|
|
184
|
-
const
|
|
185
|
-
const
|
|
186
|
-
if (
|
|
187
|
-
|
|
184
|
+
const roundedStartCoordinate = coreUtils.roundToPrecision(data.startCoordinate);
|
|
185
|
+
const roundedEndCoordinate = coreUtils.roundToPrecision(data.endCoordinate);
|
|
186
|
+
const cameraCoordinate = this._getCoordinateForViewplaneNormal(focalPoint, viewplaneNormal);
|
|
187
|
+
const roundedCameraCoordinate = coreUtils.roundToPrecision(cameraCoordinate);
|
|
188
|
+
if (roundedCameraCoordinate <
|
|
189
|
+
Math.min(roundedStartCoordinate, roundedEndCoordinate) ||
|
|
190
|
+
roundedCameraCoordinate >
|
|
191
|
+
Math.max(roundedStartCoordinate, roundedEndCoordinate)) {
|
|
188
192
|
continue;
|
|
189
193
|
}
|
|
190
|
-
|
|
191
|
-
this._throttledCalculateCachedStats(annotation, enabledElement);
|
|
192
|
-
}
|
|
193
|
-
const middleCoord = coreUtils.roundToPrecision((startCoord + endCoord) / 2);
|
|
194
|
+
const middleCoordinate = coreUtils.roundToPrecision((data.startCoordinate + data.endCoordinate) / 2);
|
|
194
195
|
let isMiddleSlice = false;
|
|
195
|
-
if (
|
|
196
|
+
if (roundedCameraCoordinate === middleCoordinate) {
|
|
196
197
|
isMiddleSlice = true;
|
|
197
198
|
}
|
|
199
|
+
data.handles.points[0][this._getIndexOfCoordinatesForViewplaneNormal(viewplaneNormal)] = middleCoordinate;
|
|
200
|
+
if (annotation.invalidated) {
|
|
201
|
+
this._throttledCalculateCachedStats(annotation, enabledElement);
|
|
202
|
+
}
|
|
198
203
|
if (!viewport.getRenderingEngine()) {
|
|
199
204
|
console.warn('Rendering Engine has been destroyed');
|
|
200
205
|
return renderStatus;
|
|
@@ -420,6 +425,9 @@ class CircleROIStartEndThresholdTool extends CircleROITool {
|
|
|
420
425
|
const targetId = this.getTargetId(viewport);
|
|
421
426
|
const imageVolume = cache.getVolume(targetId.split(/volumeId:|\?/)[1]);
|
|
422
427
|
this._computeProjectionPoints(annotation, imageVolume);
|
|
428
|
+
if (this.configuration.calculatePointsInsideVolume) {
|
|
429
|
+
this._computePointsInsideVolume(annotation, imageVolume, targetId, enabledElement);
|
|
430
|
+
}
|
|
423
431
|
annotation.invalidated = false;
|
|
424
432
|
triggerAnnotationModified(annotation, viewport.element);
|
|
425
433
|
return cachedStats;
|
|
@@ -172,9 +172,15 @@ class RectangleROIStartEndThresholdTool extends RectangleROITool {
|
|
|
172
172
|
let endCoord = endCoordinate;
|
|
173
173
|
if (Array.isArray(startCoordinate)) {
|
|
174
174
|
startCoord = this._getCoordinateForViewplaneNormal(startCoord, viewplaneNormal);
|
|
175
|
+
data.startCoordinate = startCoord;
|
|
176
|
+
data.handles.points[0][this._getIndexOfCoordinatesForViewplaneNormal(viewplaneNormal)] = startCoord;
|
|
177
|
+
data.startCoordinate = startCoord;
|
|
178
|
+
data.handles.points[0][this._getIndexOfCoordinatesForViewplaneNormal(viewplaneNormal)] = startCoord;
|
|
175
179
|
}
|
|
176
180
|
if (Array.isArray(endCoordinate)) {
|
|
177
181
|
endCoord = this._getCoordinateForViewplaneNormal(endCoord, viewplaneNormal);
|
|
182
|
+
data.endCoordinate = endCoord;
|
|
183
|
+
data.endCoordinate = endCoord;
|
|
178
184
|
}
|
|
179
185
|
const roundedStartCoord = csUtils.roundToPrecision(startCoord);
|
|
180
186
|
const roundedEndCoord = csUtils.roundToPrecision(endCoord);
|
|
@@ -378,6 +384,12 @@ class RectangleROIStartEndThresholdTool extends RectangleROITool {
|
|
|
378
384
|
const targetId = this.getTargetId(viewport);
|
|
379
385
|
const imageVolume = cache.getVolume(targetId.split(/volumeId:|\?/)[1]);
|
|
380
386
|
this._computeProjectionPoints(annotation, imageVolume);
|
|
387
|
+
if (this.configuration.calculatePointsInsideVolume) {
|
|
388
|
+
this._computePointsInsideVolume(annotation, targetId, imageVolume, enabledElement);
|
|
389
|
+
}
|
|
390
|
+
if (this.configuration.calculatePointsInsideVolume) {
|
|
391
|
+
this._computePointsInsideVolume(annotation, targetId, imageVolume, enabledElement);
|
|
392
|
+
}
|
|
381
393
|
annotation.invalidated = false;
|
|
382
394
|
triggerAnnotationModified(annotation, viewport.element);
|
|
383
395
|
return cachedStats;
|
|
@@ -5,9 +5,8 @@ import type ITouchPoints from './ITouchPoints';
|
|
|
5
5
|
import type IDistance from './IDistance';
|
|
6
6
|
import type { SetToolBindingsType } from './ISetToolModeOptions';
|
|
7
7
|
import type { Swipe } from '../enums/Touch';
|
|
8
|
-
import type { ToolModes } from '../enums';
|
|
8
|
+
import type { ToolModes, ChangeTypes } from '../enums';
|
|
9
9
|
import type { InterpolationROIAnnotation } from './ToolSpecificAnnotationTypes';
|
|
10
|
-
import type { ChangeTypes } from '../enums';
|
|
11
10
|
type NormalizedInteractionEventDetail = {
|
|
12
11
|
eventName: string;
|
|
13
12
|
renderingEngineId: string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,63 +1,3 @@
|
|
|
1
|
-
import type
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import type { ToolConfiguration } from '../types';
|
|
5
|
-
export default interface IToolGroup {
|
|
6
|
-
_toolInstances: Record<string, unknown>;
|
|
7
|
-
id: string;
|
|
8
|
-
viewportsInfo: Array<Types.IViewportId>;
|
|
9
|
-
toolOptions: Record<string, unknown>;
|
|
10
|
-
getViewportIds: () => string[];
|
|
11
|
-
getViewportsInfo: () => Array<Types.IViewportId>;
|
|
12
|
-
getToolInstance: {
|
|
13
|
-
(toolName: string): unknown;
|
|
14
|
-
};
|
|
15
|
-
hasTool(toolName: string): boolean;
|
|
16
|
-
addTool: {
|
|
17
|
-
(toolName: string, toolConfiguration?: ToolConfiguration): void;
|
|
18
|
-
};
|
|
19
|
-
addToolInstance: {
|
|
20
|
-
(toolName: string, parentClassName: string, configuration?: unknown): void;
|
|
21
|
-
};
|
|
22
|
-
addViewport: {
|
|
23
|
-
(viewportId: string, renderingEngineId?: string): void;
|
|
24
|
-
};
|
|
25
|
-
removeViewports: {
|
|
26
|
-
(renderingEngineId: string, viewportId?: string): void;
|
|
27
|
-
};
|
|
28
|
-
setToolActive: {
|
|
29
|
-
(toolName: string, toolBindingsOption?: SetToolBindingsType): void;
|
|
30
|
-
};
|
|
31
|
-
setToolPassive: {
|
|
32
|
-
(toolName: string, options?: {
|
|
33
|
-
removeAllBindings?: boolean | IToolBinding[];
|
|
34
|
-
}): void;
|
|
35
|
-
};
|
|
36
|
-
setToolEnabled: {
|
|
37
|
-
(toolName: string): void;
|
|
38
|
-
};
|
|
39
|
-
setToolDisabled: {
|
|
40
|
-
(toolName: string): void;
|
|
41
|
-
};
|
|
42
|
-
getToolOptions: {
|
|
43
|
-
(toolName: string): ToolOptionsType;
|
|
44
|
-
};
|
|
45
|
-
getActivePrimaryMouseButtonTool: {
|
|
46
|
-
(): undefined | string;
|
|
47
|
-
};
|
|
48
|
-
setViewportsCursorByToolName: {
|
|
49
|
-
(toolName: string, strategyName?: string): void;
|
|
50
|
-
};
|
|
51
|
-
setToolConfiguration: {
|
|
52
|
-
(toolName: string, configuration: ToolConfiguration, overwrite?: boolean): void;
|
|
53
|
-
};
|
|
54
|
-
getToolConfiguration: {
|
|
55
|
-
(toolName: string, configurationPath?: string): unknown;
|
|
56
|
-
};
|
|
57
|
-
getDefaultMousePrimary: {
|
|
58
|
-
(): MouseBindings;
|
|
59
|
-
};
|
|
60
|
-
clone: {
|
|
61
|
-
(newToolGroupId: string, fnToolFilter: (toolName: string) => boolean): IToolGroup;
|
|
62
|
-
};
|
|
63
|
-
}
|
|
1
|
+
import type ToolGroup from '../store/ToolGroupManager/ToolGroup';
|
|
2
|
+
type IToolGroup = ToolGroup;
|
|
3
|
+
export type { IToolGroup as default };
|