@cornerstonejs/tools 1.51.5 → 1.52.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/drawingSvg/drawPolyline.js +1 -1
- package/dist/cjs/drawingSvg/drawPolyline.js.map +1 -1
- package/dist/cjs/enums/ChangeTypes.d.ts +2 -1
- package/dist/cjs/enums/ChangeTypes.js +1 -0
- package/dist/cjs/enums/ChangeTypes.js.map +1 -1
- package/dist/cjs/eventDispatchers/keyboardEventHandlers/keyDown.js +1 -1
- package/dist/cjs/eventDispatchers/keyboardEventHandlers/keyDown.js.map +1 -1
- package/dist/cjs/stateManagement/annotation/helpers/state.d.ts +2 -1
- package/dist/cjs/stateManagement/annotation/helpers/state.js +2 -1
- package/dist/cjs/stateManagement/annotation/helpers/state.js.map +1 -1
- package/dist/cjs/tools/annotation/LivewireContourSegmentationTool.d.ts +4 -0
- package/dist/cjs/tools/annotation/LivewireContourSegmentationTool.js +82 -0
- package/dist/cjs/tools/annotation/LivewireContourSegmentationTool.js.map +1 -1
- package/dist/cjs/tools/annotation/LivewireContourTool.d.ts +10 -9
- package/dist/cjs/tools/annotation/LivewireContourTool.js +56 -43
- package/dist/cjs/tools/annotation/LivewireContourTool.js.map +1 -1
- package/dist/cjs/tools/annotation/PlanarFreehandROITool.js +2 -1
- package/dist/cjs/tools/annotation/PlanarFreehandROITool.js.map +1 -1
- package/dist/cjs/tools/annotation/SplineROITool.js +3 -3
- package/dist/cjs/tools/annotation/SplineROITool.js.map +1 -1
- package/dist/cjs/types/ContourSegmentationAnnotation.d.ts +8 -0
- package/dist/cjs/types/InterpolationTypes.d.ts +2 -0
- package/dist/cjs/types/ToolSpecificAnnotationTypes.d.ts +2 -5
- package/dist/cjs/utilities/contours/interpolation/acceptAutogeneratedInterpolations.js.map +1 -1
- package/dist/cjs/utilities/contours/interpolation/createPolylineToolData.js +2 -1
- package/dist/cjs/utilities/contours/interpolation/createPolylineToolData.js.map +1 -1
- package/dist/cjs/utilities/contours/interpolation/findAnnotationForInterpolation.js +2 -1
- package/dist/cjs/utilities/contours/interpolation/findAnnotationForInterpolation.js.map +1 -1
- package/dist/cjs/utilities/contours/interpolation/getInterpolationData.js +3 -1
- package/dist/cjs/utilities/contours/interpolation/getInterpolationData.js.map +1 -1
- package/dist/cjs/utilities/contours/interpolation/interpolate.d.ts +8 -0
- package/dist/cjs/utilities/contours/interpolation/interpolate.js +56 -42
- package/dist/cjs/utilities/contours/interpolation/interpolate.js.map +1 -1
- package/dist/cjs/utilities/contours/interpolation/selectHandles.d.ts +4 -0
- package/dist/cjs/utilities/contours/interpolation/selectHandles.js +170 -0
- package/dist/cjs/utilities/contours/interpolation/selectHandles.js.map +1 -0
- package/dist/cjs/utilities/livewire/LivewireScissors.d.ts +2 -1
- package/dist/cjs/utilities/livewire/LivewireScissors.js +46 -24
- package/dist/cjs/utilities/livewire/LivewireScissors.js.map +1 -1
- package/dist/cjs/utilities/segmentation/InterpolationManager/InterpolationManager.js +13 -3
- package/dist/cjs/utilities/segmentation/InterpolationManager/InterpolationManager.js.map +1 -1
- package/dist/esm/drawingSvg/drawPolyline.js +1 -1
- package/dist/esm/drawingSvg/drawPolyline.js.map +1 -1
- package/dist/esm/enums/ChangeTypes.js +1 -0
- package/dist/esm/enums/ChangeTypes.js.map +1 -1
- package/dist/esm/eventDispatchers/keyboardEventHandlers/keyDown.js +1 -1
- package/dist/esm/eventDispatchers/keyboardEventHandlers/keyDown.js.map +1 -1
- package/dist/esm/stateManagement/annotation/helpers/state.js +3 -2
- package/dist/esm/stateManagement/annotation/helpers/state.js.map +1 -1
- package/dist/esm/tools/annotation/LivewireContourSegmentationTool.js +81 -0
- package/dist/esm/tools/annotation/LivewireContourSegmentationTool.js.map +1 -1
- package/dist/esm/tools/annotation/LivewireContourTool.js +57 -44
- package/dist/esm/tools/annotation/LivewireContourTool.js.map +1 -1
- package/dist/esm/tools/annotation/PlanarFreehandROITool.js +2 -1
- package/dist/esm/tools/annotation/PlanarFreehandROITool.js.map +1 -1
- package/dist/esm/tools/annotation/SplineROITool.js +3 -3
- package/dist/esm/tools/annotation/SplineROITool.js.map +1 -1
- package/dist/esm/utilities/contours/interpolation/acceptAutogeneratedInterpolations.js.map +1 -1
- package/dist/esm/utilities/contours/interpolation/createPolylineToolData.js +2 -1
- package/dist/esm/utilities/contours/interpolation/createPolylineToolData.js.map +1 -1
- package/dist/esm/utilities/contours/interpolation/findAnnotationForInterpolation.js +2 -1
- package/dist/esm/utilities/contours/interpolation/findAnnotationForInterpolation.js.map +1 -1
- package/dist/esm/utilities/contours/interpolation/getInterpolationData.js +3 -1
- package/dist/esm/utilities/contours/interpolation/getInterpolationData.js.map +1 -1
- package/dist/esm/utilities/contours/interpolation/interpolate.js +57 -43
- package/dist/esm/utilities/contours/interpolation/interpolate.js.map +1 -1
- package/dist/esm/utilities/contours/interpolation/selectHandles.js +164 -0
- package/dist/esm/utilities/contours/interpolation/selectHandles.js.map +1 -0
- package/dist/esm/utilities/livewire/LivewireScissors.js +46 -24
- package/dist/esm/utilities/livewire/LivewireScissors.js.map +1 -1
- package/dist/esm/utilities/segmentation/InterpolationManager/InterpolationManager.js +13 -3
- package/dist/esm/utilities/segmentation/InterpolationManager/InterpolationManager.js.map +1 -1
- package/dist/types/enums/ChangeTypes.d.ts +2 -1
- package/dist/types/enums/ChangeTypes.d.ts.map +1 -1
- package/dist/types/stateManagement/annotation/helpers/state.d.ts +2 -1
- package/dist/types/stateManagement/annotation/helpers/state.d.ts.map +1 -1
- package/dist/types/tools/annotation/LivewireContourSegmentationTool.d.ts +4 -0
- package/dist/types/tools/annotation/LivewireContourSegmentationTool.d.ts.map +1 -1
- package/dist/types/tools/annotation/LivewireContourTool.d.ts +10 -9
- package/dist/types/tools/annotation/LivewireContourTool.d.ts.map +1 -1
- package/dist/types/tools/annotation/PlanarFreehandROITool.d.ts.map +1 -1
- package/dist/types/types/ContourSegmentationAnnotation.d.ts +8 -0
- package/dist/types/types/ContourSegmentationAnnotation.d.ts.map +1 -1
- package/dist/types/types/InterpolationTypes.d.ts +2 -0
- package/dist/types/types/InterpolationTypes.d.ts.map +1 -1
- package/dist/types/types/ToolSpecificAnnotationTypes.d.ts +2 -5
- package/dist/types/types/ToolSpecificAnnotationTypes.d.ts.map +1 -1
- package/dist/types/utilities/contours/interpolation/acceptAutogeneratedInterpolations.d.ts.map +1 -1
- package/dist/types/utilities/contours/interpolation/createPolylineToolData.d.ts.map +1 -1
- package/dist/types/utilities/contours/interpolation/findAnnotationForInterpolation.d.ts.map +1 -1
- package/dist/types/utilities/contours/interpolation/getInterpolationData.d.ts.map +1 -1
- package/dist/types/utilities/contours/interpolation/interpolate.d.ts +8 -0
- package/dist/types/utilities/contours/interpolation/interpolate.d.ts.map +1 -1
- package/dist/types/utilities/contours/interpolation/selectHandles.d.ts +5 -0
- package/dist/types/utilities/contours/interpolation/selectHandles.d.ts.map +1 -0
- package/dist/types/utilities/livewire/LivewireScissors.d.ts +2 -1
- package/dist/types/utilities/livewire/LivewireScissors.d.ts.map +1 -1
- package/dist/types/utilities/segmentation/InterpolationManager/InterpolationManager.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/drawingSvg/drawPolyline.ts +1 -1
- package/src/enums/ChangeTypes.ts +4 -0
- package/src/eventDispatchers/keyboardEventHandlers/keyDown.ts +1 -1
- package/src/stateManagement/annotation/helpers/state.ts +4 -2
- package/src/tools/annotation/LivewireContourSegmentationTool.ts +151 -0
- package/src/tools/annotation/LivewireContourTool.ts +113 -82
- package/src/tools/annotation/PlanarFreehandROITool.ts +8 -3
- package/src/tools/annotation/SplineROITool.ts +3 -3
- package/src/types/ContourSegmentationAnnotation.ts +38 -0
- package/src/types/InterpolationTypes.ts +6 -0
- package/src/types/ToolSpecificAnnotationTypes.ts +7 -5
- package/src/utilities/contours/interpolation/acceptAutogeneratedInterpolations.ts +3 -1
- package/src/utilities/contours/interpolation/createPolylineToolData.ts +7 -1
- package/src/utilities/contours/interpolation/findAnnotationForInterpolation.ts +2 -1
- package/src/utilities/contours/interpolation/getInterpolationData.ts +3 -1
- package/src/utilities/contours/interpolation/interpolate.ts +94 -75
- package/src/utilities/contours/interpolation/selectHandles.ts +240 -0
- package/src/utilities/livewire/LivewireScissors.ts +65 -53
- package/src/utilities/segmentation/InterpolationManager/InterpolationManager.ts +39 -4
- package/dist/cjs/utilities/contours/PointsArray.d.ts +0 -29
- package/dist/cjs/utilities/contours/PointsArray.js +0 -104
- package/dist/cjs/utilities/contours/PointsArray.js.map +0 -1
- package/dist/esm/utilities/contours/PointsArray.js +0 -98
- package/dist/esm/utilities/contours/PointsArray.js.map +0 -1
- package/dist/types/utilities/contours/PointsArray.d.ts +0 -30
- package/dist/types/utilities/contours/PointsArray.d.ts.map +0 -1
- package/src/utilities/contours/PointsArray.ts +0 -165
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cornerstonejs/tools",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.52.0",
|
|
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.
|
|
32
|
+
"@cornerstonejs/core": "^1.52.0",
|
|
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": "f09289b1bbae632126eec5ba208dd89da1923a4d"
|
|
57
57
|
}
|
|
@@ -53,7 +53,7 @@ export default function drawPolyline(
|
|
|
53
53
|
let pointsAttribute = '';
|
|
54
54
|
|
|
55
55
|
for (const point of points) {
|
|
56
|
-
pointsAttribute += `${point[0]}, ${point[1]} `;
|
|
56
|
+
pointsAttribute += `${point[0].toFixed(1)}, ${point[1].toFixed(1)} `;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
if (options.connectLastToFirst) {
|
package/src/enums/ChangeTypes.ts
CHANGED
|
@@ -27,6 +27,10 @@ enum ChangeTypes {
|
|
|
27
27
|
* Completed occurs only for the annotation completed event, just to identify it
|
|
28
28
|
*/
|
|
29
29
|
Completed = 'Completed',
|
|
30
|
+
/**
|
|
31
|
+
* Occurs when an interpolation result is updated with more tool specific data.
|
|
32
|
+
*/
|
|
33
|
+
InterpolationUpdated = 'InterpolationUpdated',
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
export default ChangeTypes;
|
|
@@ -40,7 +40,7 @@ export default function keyDown(evt: KeyDownEventType): void {
|
|
|
40
40
|
// so that the method can depend on the specific configuration in use.
|
|
41
41
|
const method =
|
|
42
42
|
typeof value.method === 'function' ? value.method : key[value.method];
|
|
43
|
-
method.call(key, element, value);
|
|
43
|
+
method.call(key, element, value, evt);
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
eventTarget,
|
|
5
5
|
getEnabledElementByIds,
|
|
6
6
|
} from '@cornerstonejs/core';
|
|
7
|
-
import { Events } from '../../../enums';
|
|
7
|
+
import { Events, ChangeTypes } from '../../../enums';
|
|
8
8
|
import { Annotation } from '../../../types/AnnotationTypes';
|
|
9
9
|
import { getToolGroupsWithToolName } from '../../../store/ToolGroupManager';
|
|
10
10
|
import {
|
|
@@ -85,7 +85,8 @@ function triggerAnnotationAddedForFOR(annotation: Annotation) {
|
|
|
85
85
|
*/
|
|
86
86
|
function triggerAnnotationModified(
|
|
87
87
|
annotation: Annotation,
|
|
88
|
-
element: HTMLDivElement
|
|
88
|
+
element: HTMLDivElement,
|
|
89
|
+
changeType = ChangeTypes.HandlesUpdated
|
|
89
90
|
): void {
|
|
90
91
|
const enabledElement = getEnabledElement(element);
|
|
91
92
|
const { viewportId, renderingEngineId } = enabledElement;
|
|
@@ -94,6 +95,7 @@ function triggerAnnotationModified(
|
|
|
94
95
|
annotation,
|
|
95
96
|
viewportId,
|
|
96
97
|
renderingEngineId,
|
|
98
|
+
changeType,
|
|
97
99
|
};
|
|
98
100
|
|
|
99
101
|
triggerEvent(eventTarget, eventType, eventDetail);
|
|
@@ -1,8 +1,159 @@
|
|
|
1
|
+
import type { Types } from '@cornerstonejs/core';
|
|
2
|
+
import { utilities as csUtils } from '@cornerstonejs/core';
|
|
3
|
+
|
|
1
4
|
import LivewireContourTool from './LivewireContourTool';
|
|
5
|
+
import { LivewirePath } from '../../utilities/livewire/LiveWirePath';
|
|
6
|
+
import { triggerAnnotationModified } from '../../stateManagement/annotation/helpers/state';
|
|
7
|
+
import { ChangeTypes } from '../../enums';
|
|
8
|
+
import type { ContourSegmentationAnnotation } from '../../types';
|
|
9
|
+
import { drawPolyline as drawPolylineSvg } from '../../drawingSvg';
|
|
2
10
|
|
|
3
11
|
class LivewireContourSegmentationTool extends LivewireContourTool {
|
|
4
12
|
static toolName;
|
|
5
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Updates the interpolated annotations with the currently displayed image data,
|
|
16
|
+
* performing hte livewire on the image data as generated.
|
|
17
|
+
* Note - this function is only called for interpolated livewire SEGMENTATION
|
|
18
|
+
* objects, and will return immediately otherwise.
|
|
19
|
+
*
|
|
20
|
+
* The work for the interpolation is performed in a microtask, enabling this
|
|
21
|
+
* method to return quickly for faster render speeds, but ensuring that the
|
|
22
|
+
* annotation data isn't updated before the changes are performed. The removes
|
|
23
|
+
* some irritating flickering on navigation.
|
|
24
|
+
*/
|
|
25
|
+
public updateInterpolatedAnnotation(
|
|
26
|
+
annotation: ContourSegmentationAnnotation,
|
|
27
|
+
enabledElement: Types.IEnabledElement
|
|
28
|
+
) {
|
|
29
|
+
// The interpolation sources is used as a flag here - a true livewire
|
|
30
|
+
// behaviour would be to perform a livewire between the two planes
|
|
31
|
+
// closest to this plane for each point, and use that handle. That is
|
|
32
|
+
// oblique, however, which is not currently supported.
|
|
33
|
+
if (
|
|
34
|
+
this.editData ||
|
|
35
|
+
!annotation.invalidated ||
|
|
36
|
+
!annotation.data.handles.interpolationSources
|
|
37
|
+
) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
annotation.data.contour.originalPolyline = annotation.data.contour.polyline;
|
|
41
|
+
|
|
42
|
+
// See docs above for why this is a microtask
|
|
43
|
+
queueMicrotask(() => {
|
|
44
|
+
if (!annotation.data.handles.interpolationSources) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const { points } = annotation.data.handles;
|
|
48
|
+
|
|
49
|
+
const { element } = enabledElement.viewport;
|
|
50
|
+
this.setupBaseEditData(points[0], element, annotation);
|
|
51
|
+
const { length: count } = points;
|
|
52
|
+
const { scissors } = this;
|
|
53
|
+
const { nearestEdge, repeatInterpolation } =
|
|
54
|
+
this.configuration.interpolation;
|
|
55
|
+
annotation.data.handles.originalPoints = points;
|
|
56
|
+
const { worldToSlice, sliceToWorld } = this.editData;
|
|
57
|
+
const handleSmoothing = [];
|
|
58
|
+
|
|
59
|
+
// New path generation - go through the handles and regenerate the polyline
|
|
60
|
+
if (nearestEdge) {
|
|
61
|
+
let lastPoint = worldToSlice(points[points.length - 1]);
|
|
62
|
+
// Nearest edge handling
|
|
63
|
+
points.forEach((point, hIndex) => {
|
|
64
|
+
const testPoint = worldToSlice(point);
|
|
65
|
+
lastPoint = testPoint;
|
|
66
|
+
handleSmoothing.push(testPoint);
|
|
67
|
+
|
|
68
|
+
// Fill the costs buffer and then find the minimum cost
|
|
69
|
+
// This is a little too aggressive about pulling the line in
|
|
70
|
+
scissors.startSearch(lastPoint);
|
|
71
|
+
scissors.findPathToPoint(testPoint);
|
|
72
|
+
// Fill the costs for a point a bit further along by searching for a
|
|
73
|
+
// point further along.
|
|
74
|
+
scissors.findPathToPoint(
|
|
75
|
+
worldToSlice(points[(hIndex + 3) % points.length])
|
|
76
|
+
);
|
|
77
|
+
const minPoint = scissors.findMinNearby(testPoint, nearestEdge);
|
|
78
|
+
if (!csUtils.isEqual(testPoint, minPoint)) {
|
|
79
|
+
handleSmoothing[hIndex] = minPoint;
|
|
80
|
+
lastPoint = minPoint;
|
|
81
|
+
points[hIndex] = sliceToWorld(minPoint);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Regenerate the updated data based on the updated handles
|
|
87
|
+
const acceptedPath = new LivewirePath();
|
|
88
|
+
for (let i = 0; i < count; i++) {
|
|
89
|
+
scissors.startSearch(worldToSlice(points[i]));
|
|
90
|
+
const path = scissors.findPathToPoint(
|
|
91
|
+
worldToSlice(points[(i + 1) % count])
|
|
92
|
+
);
|
|
93
|
+
acceptedPath.addPoints(path);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Now, update the rendering
|
|
97
|
+
this.updateAnnotation(element, acceptedPath);
|
|
98
|
+
this.scissors = null;
|
|
99
|
+
this.scissorsRight = null;
|
|
100
|
+
this.editData = null;
|
|
101
|
+
annotation.data.handles.interpolationSources = null;
|
|
102
|
+
|
|
103
|
+
if (repeatInterpolation) {
|
|
104
|
+
triggerAnnotationModified(
|
|
105
|
+
annotation,
|
|
106
|
+
enabledElement.viewport.element,
|
|
107
|
+
ChangeTypes.InterpolationUpdated
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Adds the update to the interpolated annotaiton on render an instance,
|
|
115
|
+
* but otherwise just calls the parent render annotation instance.
|
|
116
|
+
*/
|
|
117
|
+
protected renderAnnotationInstance(renderContext): boolean {
|
|
118
|
+
const { enabledElement, svgDrawingHelper } = renderContext;
|
|
119
|
+
const annotation =
|
|
120
|
+
renderContext.annotation as ContourSegmentationAnnotation;
|
|
121
|
+
const { annotationUID } = annotation;
|
|
122
|
+
const { viewport } = enabledElement;
|
|
123
|
+
const { worldToCanvas } = viewport;
|
|
124
|
+
const { showInterpolationPolyline } =
|
|
125
|
+
this.configuration.interpolation || {};
|
|
126
|
+
|
|
127
|
+
this.updateInterpolatedAnnotation?.(annotation, enabledElement);
|
|
128
|
+
const { originalPolyline } = annotation.data.contour;
|
|
129
|
+
|
|
130
|
+
const rendered = super.renderAnnotationInstance(renderContext);
|
|
131
|
+
|
|
132
|
+
if (
|
|
133
|
+
showInterpolationPolyline &&
|
|
134
|
+
originalPolyline &&
|
|
135
|
+
annotation.autoGenerated
|
|
136
|
+
) {
|
|
137
|
+
const polylineCanvasPoints = originalPolyline.map(
|
|
138
|
+
worldToCanvas
|
|
139
|
+
) as Types.Point2[];
|
|
140
|
+
polylineCanvasPoints.push(polylineCanvasPoints[0]);
|
|
141
|
+
drawPolylineSvg(
|
|
142
|
+
svgDrawingHelper,
|
|
143
|
+
annotationUID,
|
|
144
|
+
'interpolationContour-0',
|
|
145
|
+
polylineCanvasPoints,
|
|
146
|
+
{
|
|
147
|
+
color: '#70ffff',
|
|
148
|
+
lineWidth: 1,
|
|
149
|
+
fillOpacity: 0,
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return rendered;
|
|
155
|
+
}
|
|
156
|
+
|
|
6
157
|
protected isContourSegmentationTool(): boolean {
|
|
7
158
|
// Re-enable contour segmentation behavior disabled by LivewireContourTool
|
|
8
159
|
return true;
|
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
import { vec3 } from 'gl-matrix';
|
|
2
|
-
|
|
3
2
|
import {
|
|
4
3
|
getEnabledElement,
|
|
5
|
-
triggerEvent,
|
|
6
|
-
eventTarget,
|
|
7
4
|
utilities as csUtils,
|
|
8
5
|
VolumeViewport,
|
|
9
6
|
} from '@cornerstonejs/core';
|
|
10
7
|
import type { Types } from '@cornerstonejs/core';
|
|
8
|
+
|
|
11
9
|
import { removeAnnotation } from '../../stateManagement/annotation/annotationState';
|
|
12
10
|
import { drawHandles as drawHandlesSvg } from '../../drawingSvg';
|
|
13
11
|
import { state } from '../../store';
|
|
14
12
|
import { Events, ChangeTypes } from '../../enums';
|
|
15
13
|
import { resetElementCursor } from '../../cursors/elementCursor';
|
|
16
14
|
import type {
|
|
17
|
-
Annotation,
|
|
18
15
|
EventTypes,
|
|
19
16
|
ToolHandle,
|
|
20
17
|
PublicToolProps,
|
|
@@ -25,9 +22,9 @@ import { math, triggerAnnotationRenderForViewportIds } from '../../utilities';
|
|
|
25
22
|
import findHandlePolylineIndex from '../../utilities/contours/findHandlePolylineIndex';
|
|
26
23
|
import { LivewireContourAnnotation } from '../../types/ToolSpecificAnnotationTypes';
|
|
27
24
|
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
} from '../../
|
|
25
|
+
triggerAnnotationModified,
|
|
26
|
+
triggerAnnotationCompleted,
|
|
27
|
+
} from '../../stateManagement/annotation/helpers/state';
|
|
31
28
|
import reverseIfAntiClockwise from '../../utilities/contours/reverseIfAntiClockwise';
|
|
32
29
|
|
|
33
30
|
import { LivewireScissors } from '../../utilities/livewire/LivewireScissors';
|
|
@@ -35,15 +32,13 @@ import { LivewirePath } from '../../utilities/livewire/LiveWirePath';
|
|
|
35
32
|
import { getViewportIdsWithToolToRender } from '../../utilities/viewportFilters';
|
|
36
33
|
import ContourSegmentationBaseTool from '../base/ContourSegmentationBaseTool';
|
|
37
34
|
|
|
38
|
-
const { isEqual } = csUtils;
|
|
39
|
-
|
|
40
35
|
const CLICK_CLOSE_CURVE_SQR_DIST = 10 ** 2; // px
|
|
41
36
|
|
|
42
37
|
class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
43
38
|
public static toolName: string;
|
|
44
|
-
|
|
39
|
+
protected scissors: LivewireScissors;
|
|
45
40
|
/** The scissors from the right handle, used for editing */
|
|
46
|
-
|
|
41
|
+
protected scissorsRight: LivewireScissors;
|
|
47
42
|
|
|
48
43
|
touchDragCallback: any;
|
|
49
44
|
mouseDragCallback: any;
|
|
@@ -72,6 +67,48 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
72
67
|
supportedInteractionTypes: ['Mouse', 'Touch'],
|
|
73
68
|
configuration: {
|
|
74
69
|
preventHandleOutsideImage: false,
|
|
70
|
+
/**
|
|
71
|
+
* Configuring this to a value larger than 0 will snap handles to nearby
|
|
72
|
+
* livewire points, within the given rectangle surrounding the clicked point.
|
|
73
|
+
* If set to 0, then the exact clicked point will be used instead, which may
|
|
74
|
+
* not be an edge and can result in jagged outlines.
|
|
75
|
+
* The unit is image pixels (index).
|
|
76
|
+
*/
|
|
77
|
+
snapHandleNearby: 2,
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Interpolation is only available for segmentation versions of these
|
|
81
|
+
* tools. To use it on the segmentation tools, set enabled to true,
|
|
82
|
+
* and create two livewire contours in the same segment index, separated
|
|
83
|
+
* by at least one slice.
|
|
84
|
+
*/
|
|
85
|
+
interpolation: {
|
|
86
|
+
enabled: false,
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Set the nearestEdge to snap interpolated handles to an edge within
|
|
90
|
+
* the given number of pixels. Setting to 0 disables snap to pixel
|
|
91
|
+
* for interpolation and the interpolated point will be used directly.
|
|
92
|
+
* Setting to too large a value may result in many points outside the contour
|
|
93
|
+
* being chosen.
|
|
94
|
+
*/
|
|
95
|
+
nearestEdge: 2,
|
|
96
|
+
/**
|
|
97
|
+
* Set to true to show the interpolated polyline, which can be useful
|
|
98
|
+
* when understanding the nearest edge and
|
|
99
|
+
*/
|
|
100
|
+
showInterpolationPolyline: false,
|
|
101
|
+
},
|
|
102
|
+
actions: {
|
|
103
|
+
undo: {
|
|
104
|
+
method: 'undo',
|
|
105
|
+
bindings: [
|
|
106
|
+
{
|
|
107
|
+
key: 'Escape',
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
},
|
|
75
112
|
},
|
|
76
113
|
}
|
|
77
114
|
) {
|
|
@@ -214,7 +251,7 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
214
251
|
const { currentPoints, element } = eventDetail;
|
|
215
252
|
const { world: worldPos } = currentPoints;
|
|
216
253
|
const { renderingEngine } = getEnabledElement(element);
|
|
217
|
-
const annotation = this.createAnnotation(evt)
|
|
254
|
+
const annotation = this.createAnnotation(evt);
|
|
218
255
|
|
|
219
256
|
this.setupBaseEditData(worldPos, element, annotation);
|
|
220
257
|
this.addAnnotation(annotation, element);
|
|
@@ -335,7 +372,10 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
335
372
|
evt.preventDefault();
|
|
336
373
|
};
|
|
337
374
|
|
|
338
|
-
_endCallback = (
|
|
375
|
+
_endCallback = (
|
|
376
|
+
evt: EventTypes.InteractionEventType,
|
|
377
|
+
clearAnnotation = false
|
|
378
|
+
): void => {
|
|
339
379
|
const eventDetail = evt.detail;
|
|
340
380
|
const { element } = eventDetail;
|
|
341
381
|
|
|
@@ -353,10 +393,17 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
353
393
|
const { renderingEngine } = enabledElement;
|
|
354
394
|
|
|
355
395
|
if (
|
|
356
|
-
this.isHandleOutsideImage &&
|
|
357
|
-
|
|
396
|
+
(this.isHandleOutsideImage &&
|
|
397
|
+
this.configuration.preventHandleOutsideImage) ||
|
|
398
|
+
clearAnnotation
|
|
358
399
|
) {
|
|
359
400
|
removeAnnotation(annotation.annotationUID);
|
|
401
|
+
this.clearEditData();
|
|
402
|
+
triggerAnnotationRenderForViewportIds(
|
|
403
|
+
renderingEngine,
|
|
404
|
+
viewportIdsToRender
|
|
405
|
+
);
|
|
406
|
+
return;
|
|
360
407
|
}
|
|
361
408
|
|
|
362
409
|
// Reverse the points if needed, ensuring both the handles and the
|
|
@@ -377,47 +424,15 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
377
424
|
: ChangeTypes.HandlesUpdated;
|
|
378
425
|
|
|
379
426
|
this.triggerChangeEvent(annotation, enabledElement, changeType);
|
|
427
|
+
this.clearEditData();
|
|
428
|
+
};
|
|
380
429
|
|
|
430
|
+
protected clearEditData() {
|
|
381
431
|
this.editData = null;
|
|
382
432
|
this.scissors = null;
|
|
383
433
|
this.scissorsRight = null;
|
|
384
434
|
this.isDrawing = false;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* Triggers an annotation completed event.
|
|
389
|
-
*/
|
|
390
|
-
triggerAnnotationCompleted = (
|
|
391
|
-
annotation: LivewireContourAnnotation
|
|
392
|
-
): void => {
|
|
393
|
-
const eventType = Events.ANNOTATION_COMPLETED;
|
|
394
|
-
const eventDetail: AnnotationCompletedEventDetail = {
|
|
395
|
-
annotation,
|
|
396
|
-
changeType: ChangeTypes.Completed,
|
|
397
|
-
};
|
|
398
|
-
|
|
399
|
-
triggerEvent(eventTarget, eventType, eventDetail);
|
|
400
|
-
};
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* Triggers an annotation modified event.
|
|
404
|
-
*/
|
|
405
|
-
triggerAnnotationModified = (
|
|
406
|
-
annotation: LivewireContourAnnotation,
|
|
407
|
-
enabledElement: Types.IEnabledElement,
|
|
408
|
-
changeType = ChangeTypes.StatsUpdated
|
|
409
|
-
): void => {
|
|
410
|
-
const { viewportId, renderingEngineId } = enabledElement;
|
|
411
|
-
const eventType = Events.ANNOTATION_MODIFIED;
|
|
412
|
-
const eventDetail: AnnotationModifiedEventDetail = {
|
|
413
|
-
annotation,
|
|
414
|
-
viewportId,
|
|
415
|
-
renderingEngineId,
|
|
416
|
-
changeType,
|
|
417
|
-
};
|
|
418
|
-
|
|
419
|
-
triggerEvent(eventTarget, eventType, eventDetail);
|
|
420
|
-
};
|
|
435
|
+
}
|
|
421
436
|
|
|
422
437
|
/**
|
|
423
438
|
* Triggers an annotation complete or modified event based on changeType.
|
|
@@ -428,9 +443,13 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
428
443
|
changeType = ChangeTypes.StatsUpdated
|
|
429
444
|
): void => {
|
|
430
445
|
if (changeType === ChangeTypes.Completed) {
|
|
431
|
-
|
|
446
|
+
triggerAnnotationCompleted(annotation);
|
|
432
447
|
} else {
|
|
433
|
-
|
|
448
|
+
triggerAnnotationModified(
|
|
449
|
+
annotation,
|
|
450
|
+
enabledElement.viewport.element,
|
|
451
|
+
changeType
|
|
452
|
+
);
|
|
434
453
|
}
|
|
435
454
|
};
|
|
436
455
|
|
|
@@ -485,25 +504,24 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
485
504
|
}
|
|
486
505
|
}
|
|
487
506
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
this.
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
if (smoothPathCount) {
|
|
496
|
-
this.editData.currentPath.removeLastPoints(smoothPathCount);
|
|
497
|
-
annotation.data.contour.polyline.splice(
|
|
498
|
-
annotation.data.contour.polyline.length - smoothPathCount,
|
|
499
|
-
smoothPathCount
|
|
507
|
+
const { snapHandleNearby } = this.configuration;
|
|
508
|
+
// Snap the handles as they get created, but not during edit
|
|
509
|
+
if (snapHandleNearby && !this.editData.closed) {
|
|
510
|
+
const currentPath = new LivewirePath();
|
|
511
|
+
const snapPoint = this.scissors.findMinNearby(
|
|
512
|
+
worldToSlice(worldPosOriginal),
|
|
513
|
+
1
|
|
500
514
|
);
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
515
|
+
const pathPoints = this.scissors.findPathToPoint(snapPoint);
|
|
516
|
+
currentPath.addPoints(pathPoints);
|
|
517
|
+
currentPath.prependPath(this.editData.confirmedPath);
|
|
518
|
+
worldPos = sliceToWorld(snapPoint);
|
|
519
|
+
this.editData.currentPath = currentPath;
|
|
505
520
|
}
|
|
506
521
|
|
|
522
|
+
this.editData.closed = this.editData.closed || closePath;
|
|
523
|
+
this.editData.confirmedPath = this.editData.currentPath;
|
|
524
|
+
|
|
507
525
|
// Add the current cursor position as a new control point after clicking
|
|
508
526
|
const lastPoint = this.editData.currentPath.getLastPoint();
|
|
509
527
|
|
|
@@ -518,7 +536,7 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
518
536
|
|
|
519
537
|
if (this.editData.closed) {
|
|
520
538
|
// Update the annotation because `editData` will be set to null
|
|
521
|
-
this.
|
|
539
|
+
this.updateAnnotation(element, this.editData.confirmedPath);
|
|
522
540
|
this._endCallback(evt);
|
|
523
541
|
}
|
|
524
542
|
|
|
@@ -624,7 +642,7 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
624
642
|
|
|
625
643
|
const { activeHandleIndex } = data.handles;
|
|
626
644
|
if (activeHandleIndex === null || activeHandleIndex === undefined) {
|
|
627
|
-
data.
|
|
645
|
+
data.handles.activeHandleIndex = handleIndex;
|
|
628
646
|
} else if (activeHandleIndex !== handleIndex) {
|
|
629
647
|
throw new Error(
|
|
630
648
|
`Trying to edit a different handle than the one currently being edited ${handleIndex}!==${data.handles.activeHandleIndex}`
|
|
@@ -773,22 +791,22 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
773
791
|
const { element } = viewport;
|
|
774
792
|
|
|
775
793
|
// Update the annotation that is in editData (being edited)
|
|
776
|
-
this.
|
|
794
|
+
this.updateAnnotation(element, this.editData?.currentPath);
|
|
777
795
|
|
|
778
796
|
return super.renderAnnotation(enabledElement, svgDrawingHelper);
|
|
779
797
|
}
|
|
780
798
|
|
|
781
799
|
protected isContourSegmentationTool(): boolean {
|
|
782
|
-
// Disable contour
|
|
800
|
+
// Disable contour segmentation behavior because it shall be activated only
|
|
783
801
|
// for LivewireContourSegmentationTool
|
|
784
802
|
return false;
|
|
785
803
|
}
|
|
786
804
|
|
|
787
|
-
protected createAnnotation(evt: EventTypes.InteractionEventType)
|
|
805
|
+
protected createAnnotation(evt: EventTypes.InteractionEventType) {
|
|
788
806
|
const contourSegmentationAnnotation = super.createAnnotation(evt);
|
|
789
807
|
const { world: worldPos } = evt.detail.currentPoints;
|
|
790
808
|
|
|
791
|
-
|
|
809
|
+
const annotation = <LivewireContourAnnotation>csUtils.deepMerge(
|
|
792
810
|
contourSegmentationAnnotation,
|
|
793
811
|
{
|
|
794
812
|
data: {
|
|
@@ -798,6 +816,21 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
798
816
|
},
|
|
799
817
|
}
|
|
800
818
|
);
|
|
819
|
+
return annotation;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Clears any in progress edits, mostly used to get rid of accidentally started
|
|
824
|
+
* contours that happen on clicking not quite the right handle point.
|
|
825
|
+
* Eventually this is to be replaced with a proper undo, once that framework
|
|
826
|
+
* is available.
|
|
827
|
+
*/
|
|
828
|
+
public undo(element, config, evt) {
|
|
829
|
+
if (!this.editData) {
|
|
830
|
+
// TODO - proper undo
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
this._endCallback(evt, true);
|
|
801
834
|
}
|
|
802
835
|
|
|
803
836
|
/**
|
|
@@ -808,14 +841,15 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
808
841
|
protected renderAnnotationInstance(renderContext: {
|
|
809
842
|
enabledElement: Types.IEnabledElement;
|
|
810
843
|
targetId: string;
|
|
811
|
-
annotation:
|
|
844
|
+
annotation: LivewireContourAnnotation;
|
|
812
845
|
annotationStyle: Record<string, any>;
|
|
813
846
|
svgDrawingHelper: SVGDrawingHelper;
|
|
814
847
|
}): boolean {
|
|
815
|
-
const { enabledElement, svgDrawingHelper, annotationStyle } =
|
|
848
|
+
const { annotation, enabledElement, svgDrawingHelper, annotationStyle } =
|
|
849
|
+
renderContext;
|
|
850
|
+
|
|
816
851
|
const { viewport } = enabledElement;
|
|
817
852
|
const { worldToCanvas } = viewport;
|
|
818
|
-
const annotation = renderContext.annotation as LivewireContourAnnotation;
|
|
819
853
|
const { annotationUID, data, highlighted } = annotation;
|
|
820
854
|
const { handles } = data;
|
|
821
855
|
const newAnnotation = this.editData?.newAnnotation;
|
|
@@ -851,10 +885,7 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
|
|
|
851
885
|
return true;
|
|
852
886
|
}
|
|
853
887
|
|
|
854
|
-
|
|
855
|
-
element: HTMLDivElement,
|
|
856
|
-
livewirePath: LivewirePath
|
|
857
|
-
) {
|
|
888
|
+
protected updateAnnotation(_, livewirePath: LivewirePath) {
|
|
858
889
|
if (!this.editData || !livewirePath) {
|
|
859
890
|
return;
|
|
860
891
|
}
|
|
@@ -46,6 +46,7 @@ import { isViewportPreScaled } from '../../utilities/viewport/isViewportPreScale
|
|
|
46
46
|
import { getModalityUnit } from '../../utilities/getModalityUnit';
|
|
47
47
|
import { BasicStatsCalculator } from '../../utilities/math/basic';
|
|
48
48
|
import ContourSegmentationBaseTool from '../base/ContourSegmentationBaseTool';
|
|
49
|
+
import { ChangeTypes } from '../../enums';
|
|
49
50
|
|
|
50
51
|
const { pointCanProjectOnLine } = polyline;
|
|
51
52
|
const { EPSILON } = CONSTANTS;
|
|
@@ -79,7 +80,7 @@ const PARALLEL_THRESHOLD = 1 - EPSILON;
|
|
|
79
80
|
*
|
|
80
81
|
* The result of smoothing will be removal of some of the outliers
|
|
81
82
|
* Changing tool configuration (see below) you can fine-tune the smoothing process by changing knotsRatioPercentageOnAdd and knotsRatioPercentageOnEdit value, which smaller values produces a more agressive smoothing.
|
|
82
|
-
* A smaller value of knotsRatioPercentageOnAdd/knotsRatioPercentageOnEdit produces a more
|
|
83
|
+
* A smaller value of knotsRatioPercentageOnAdd/knotsRatioPercentageOnEdit produces a more aggressive smoothing.
|
|
83
84
|
*
|
|
84
85
|
* ```js
|
|
85
86
|
* cornerstoneTools.addTool(PlanarFreehandROITool)
|
|
@@ -98,7 +99,7 @@ const PARALLEL_THRESHOLD = 1 - EPSILON;
|
|
|
98
99
|
* ],
|
|
99
100
|
* })
|
|
100
101
|
*
|
|
101
|
-
* // set smoothing
|
|
102
|
+
* // set smoothing aggressiveness while adding new annotation (ps: this does not change if smoothing is ON or OFF)
|
|
102
103
|
* toolGroup.setToolConfiguration(PlanarFreehandROITool.toolName, {
|
|
103
104
|
* smoothing: { knotsRatioPercentageOnAdd: 30 },
|
|
104
105
|
* });
|
|
@@ -823,7 +824,11 @@ class PlanarFreehandROITool extends ContourSegmentationBaseTool {
|
|
|
823
824
|
};
|
|
824
825
|
}
|
|
825
826
|
|
|
826
|
-
triggerAnnotationModified(
|
|
827
|
+
triggerAnnotationModified(
|
|
828
|
+
annotation,
|
|
829
|
+
enabledElement.element,
|
|
830
|
+
ChangeTypes.StatsUpdated
|
|
831
|
+
);
|
|
827
832
|
|
|
828
833
|
annotation.invalidated = false;
|
|
829
834
|
|
|
@@ -814,9 +814,9 @@ class SplineROITool extends ContourSegmentationBaseTool {
|
|
|
814
814
|
|
|
815
815
|
// Add an action to create a new spline data on creating an interpolated
|
|
816
816
|
// instance.
|
|
817
|
-
let
|
|
817
|
+
let onInterpolationComplete;
|
|
818
818
|
if (this.configuration.interpolation?.enabled) {
|
|
819
|
-
|
|
819
|
+
onInterpolationComplete = (annotation) => {
|
|
820
820
|
annotation.data.spline ||= createSpline();
|
|
821
821
|
this.createInterpolatedSplineControl(annotation);
|
|
822
822
|
};
|
|
@@ -830,7 +830,7 @@ class SplineROITool extends ContourSegmentationBaseTool {
|
|
|
830
830
|
spline: createSpline(),
|
|
831
831
|
cachedStats: {},
|
|
832
832
|
},
|
|
833
|
-
|
|
833
|
+
onInterpolationComplete,
|
|
834
834
|
});
|
|
835
835
|
}
|
|
836
836
|
|