@cornerstonejs/tools 1.20.3 → 1.21.1
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/getSvgDrawingHelper.js +1 -1
- package/dist/cjs/drawingSvg/getSvgDrawingHelper.js.map +1 -1
- package/dist/cjs/enums/Events.d.ts +1 -0
- package/dist/cjs/enums/Events.js +1 -0
- package/dist/cjs/enums/Events.js.map +1 -1
- package/dist/cjs/eventDispatchers/mouseEventHandlers/mouseDown.js +5 -0
- package/dist/cjs/eventDispatchers/mouseEventHandlers/mouseDown.js.map +1 -1
- package/dist/cjs/eventDispatchers/mouseEventHandlers/mouseDownAnnotationAction.d.ts +2 -0
- package/dist/cjs/eventDispatchers/mouseEventHandlers/mouseDownAnnotationAction.js +41 -0
- package/dist/cjs/eventDispatchers/mouseEventHandlers/mouseDownAnnotationAction.js.map +1 -0
- package/dist/cjs/eventDispatchers/shared/getToolsWithActionsForMouseEvent.d.ts +3 -0
- package/dist/cjs/eventDispatchers/shared/getToolsWithActionsForMouseEvent.js +39 -0
- package/dist/cjs/eventDispatchers/shared/getToolsWithActionsForMouseEvent.js.map +1 -0
- package/dist/cjs/eventListeners/mouse/mouseDownListener.js +5 -1
- package/dist/cjs/eventListeners/mouse/mouseDownListener.js.map +1 -1
- package/dist/cjs/eventListeners/mouse/mouseMoveListener.js +5 -1
- package/dist/cjs/eventListeners/mouse/mouseMoveListener.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/index.js +3 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/store/ToolGroupManager/ToolGroup.d.ts +2 -0
- package/dist/cjs/store/ToolGroupManager/ToolGroup.js +35 -0
- package/dist/cjs/store/ToolGroupManager/ToolGroup.js.map +1 -1
- package/dist/cjs/tools/AdvancedMagnifyTool.d.ts +36 -0
- package/dist/cjs/tools/AdvancedMagnifyTool.js +448 -0
- package/dist/cjs/tools/AdvancedMagnifyTool.js.map +1 -0
- package/dist/cjs/tools/AdvancedMagnifyViewport.d.ts +76 -0
- package/dist/cjs/tools/AdvancedMagnifyViewport.js +352 -0
- package/dist/cjs/tools/AdvancedMagnifyViewport.js.map +1 -0
- package/dist/cjs/tools/AdvancedMagnifyViewportManager.d.ts +36 -0
- package/dist/cjs/tools/AdvancedMagnifyViewportManager.js +133 -0
- package/dist/cjs/tools/AdvancedMagnifyViewportManager.js.map +1 -0
- package/dist/cjs/tools/annotation/EllipticalROITool.d.ts +1 -1
- package/dist/cjs/tools/annotation/EllipticalROITool.js +12 -8
- package/dist/cjs/tools/annotation/EllipticalROITool.js.map +1 -1
- package/dist/cjs/tools/index.d.ts +2 -1
- package/dist/cjs/tools/index.js +3 -1
- package/dist/cjs/tools/index.js.map +1 -1
- package/dist/cjs/types/EventTypes.d.ts +9 -1
- package/dist/cjs/types/IToolGroup.d.ts +3 -0
- package/dist/cjs/types/ToolAction.d.ts +8 -0
- package/dist/cjs/types/ToolAction.js +3 -0
- package/dist/cjs/types/ToolAction.js.map +1 -0
- package/dist/cjs/types/ToolSpecificAnnotationTypes.d.ts +11 -0
- package/dist/cjs/types/index.d.ts +2 -1
- package/dist/esm/drawingSvg/getSvgDrawingHelper.js +1 -1
- package/dist/esm/drawingSvg/getSvgDrawingHelper.js.map +1 -1
- package/dist/esm/enums/Events.d.ts +1 -0
- package/dist/esm/enums/Events.js +1 -0
- package/dist/esm/enums/Events.js.map +1 -1
- package/dist/esm/eventDispatchers/mouseEventHandlers/mouseDown.js +5 -0
- package/dist/esm/eventDispatchers/mouseEventHandlers/mouseDown.js.map +1 -1
- package/dist/esm/eventDispatchers/mouseEventHandlers/mouseDownAnnotationAction.d.ts +2 -0
- package/dist/esm/eventDispatchers/mouseEventHandlers/mouseDownAnnotationAction.js +35 -0
- package/dist/esm/eventDispatchers/mouseEventHandlers/mouseDownAnnotationAction.js.map +1 -0
- package/dist/esm/eventDispatchers/shared/getToolsWithActionsForMouseEvent.d.ts +3 -0
- package/dist/esm/eventDispatchers/shared/getToolsWithActionsForMouseEvent.js +32 -0
- package/dist/esm/eventDispatchers/shared/getToolsWithActionsForMouseEvent.js.map +1 -0
- package/dist/esm/eventListeners/mouse/mouseDownListener.js +5 -1
- package/dist/esm/eventListeners/mouse/mouseDownListener.js.map +1 -1
- package/dist/esm/eventListeners/mouse/mouseMoveListener.js +5 -1
- package/dist/esm/eventListeners/mouse/mouseMoveListener.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +2 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/store/ToolGroupManager/ToolGroup.d.ts +2 -0
- package/dist/esm/store/ToolGroupManager/ToolGroup.js +35 -1
- package/dist/esm/store/ToolGroupManager/ToolGroup.js.map +1 -1
- package/dist/esm/tools/AdvancedMagnifyTool.d.ts +36 -0
- package/dist/esm/tools/AdvancedMagnifyTool.js +440 -0
- package/dist/esm/tools/AdvancedMagnifyTool.js.map +1 -0
- package/dist/esm/tools/AdvancedMagnifyViewport.d.ts +76 -0
- package/dist/esm/tools/AdvancedMagnifyViewport.js +346 -0
- package/dist/esm/tools/AdvancedMagnifyViewport.js.map +1 -0
- package/dist/esm/tools/AdvancedMagnifyViewportManager.d.ts +36 -0
- package/dist/esm/tools/AdvancedMagnifyViewportManager.js +128 -0
- package/dist/esm/tools/AdvancedMagnifyViewportManager.js.map +1 -0
- package/dist/esm/tools/annotation/EllipticalROITool.d.ts +1 -1
- package/dist/esm/tools/annotation/EllipticalROITool.js +12 -8
- package/dist/esm/tools/annotation/EllipticalROITool.js.map +1 -1
- package/dist/esm/tools/index.d.ts +2 -1
- package/dist/esm/tools/index.js +2 -1
- package/dist/esm/tools/index.js.map +1 -1
- package/dist/esm/types/EventTypes.d.ts +9 -1
- package/dist/esm/types/IToolGroup.d.ts +3 -0
- package/dist/esm/types/ToolAction.d.ts +8 -0
- package/dist/esm/types/ToolAction.js +2 -0
- package/dist/esm/types/ToolAction.js.map +1 -0
- package/dist/esm/types/ToolSpecificAnnotationTypes.d.ts +11 -0
- package/dist/esm/types/index.d.ts +2 -1
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +3 -3
- package/src/drawingSvg/getSvgDrawingHelper.ts +4 -1
- package/src/enums/Events.ts +9 -0
- package/src/eventDispatchers/mouseEventHandlers/mouseDown.ts +10 -1
- package/src/eventDispatchers/mouseEventHandlers/mouseDownAnnotationAction.ts +72 -0
- package/src/eventDispatchers/shared/getToolsWithActionsForMouseEvent.ts +66 -0
- package/src/eventListeners/mouse/mouseDownListener.ts +7 -1
- package/src/eventListeners/mouse/mouseMoveListener.ts +7 -1
- package/src/index.ts +2 -0
- package/src/store/ToolGroupManager/ToolGroup.ts +79 -2
- package/src/tools/AdvancedMagnifyTool.ts +725 -0
- package/src/tools/AdvancedMagnifyViewport.ts +624 -0
- package/src/tools/AdvancedMagnifyViewportManager.ts +291 -0
- package/src/tools/annotation/EllipticalROITool.ts +14 -9
- package/src/tools/index.ts +2 -0
- package/src/types/EventTypes.ts +23 -0
- package/src/types/IToolGroup.ts +7 -0
- package/src/types/ToolAction.ts +54 -0
- package/src/types/ToolSpecificAnnotationTypes.ts +12 -0
- package/src/types/index.ts +2 -0
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
import { AnnotationTool } from './base';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getEnabledElement,
|
|
5
|
+
eventTarget,
|
|
6
|
+
triggerEvent,
|
|
7
|
+
utilities as csUtils,
|
|
8
|
+
} from '@cornerstonejs/core';
|
|
9
|
+
import type { Types } from '@cornerstonejs/core';
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
addAnnotation,
|
|
13
|
+
getAnnotations,
|
|
14
|
+
} from '../stateManagement/annotation/annotationState';
|
|
15
|
+
import { isAnnotationLocked } from '../stateManagement/annotation/annotationLocking';
|
|
16
|
+
import { isAnnotationVisible } from '../stateManagement/annotation/annotationVisibility';
|
|
17
|
+
import {
|
|
18
|
+
drawCircle as drawCircleSvg,
|
|
19
|
+
drawHandles as drawHandlesSvg,
|
|
20
|
+
} from '../drawingSvg';
|
|
21
|
+
import { state } from '../store';
|
|
22
|
+
import { Events, MouseBindings, KeyboardBindings } from '../enums';
|
|
23
|
+
import { getViewportIdsWithToolToRender } from '../utilities/viewportFilters';
|
|
24
|
+
import {
|
|
25
|
+
resetElementCursor,
|
|
26
|
+
hideElementCursor,
|
|
27
|
+
} from '../cursors/elementCursor';
|
|
28
|
+
import {
|
|
29
|
+
EventTypes,
|
|
30
|
+
ToolHandle,
|
|
31
|
+
PublicToolProps,
|
|
32
|
+
ToolProps,
|
|
33
|
+
SVGDrawingHelper,
|
|
34
|
+
} from '../types';
|
|
35
|
+
import { AdvancedMagnifyAnnotation } from '../types/ToolSpecificAnnotationTypes';
|
|
36
|
+
|
|
37
|
+
import { AnnotationCompletedEventDetail } from '../types/EventTypes';
|
|
38
|
+
import triggerAnnotationRenderForViewportIds from '../utilities/triggerAnnotationRenderForViewportIds';
|
|
39
|
+
import { StyleSpecifier } from '../types/AnnotationStyle';
|
|
40
|
+
import { getCanvasCircleRadius } from '../utilities/math/circle';
|
|
41
|
+
import AdvancedMagnifyViewportManager from './AdvancedMagnifyViewportManager';
|
|
42
|
+
import type { AutoPanCallbackData } from './AdvancedMagnifyViewport';
|
|
43
|
+
|
|
44
|
+
class AdvancedMagnifyTool extends AnnotationTool {
|
|
45
|
+
static toolName;
|
|
46
|
+
|
|
47
|
+
magnifyViewportManager: AdvancedMagnifyViewportManager;
|
|
48
|
+
touchDragCallback: any;
|
|
49
|
+
mouseDragCallback: any;
|
|
50
|
+
editData: {
|
|
51
|
+
annotation: any;
|
|
52
|
+
viewportIdsToRender: Array<string>;
|
|
53
|
+
handleIndex?: number;
|
|
54
|
+
newAnnotation?: boolean;
|
|
55
|
+
hasMoved?: boolean;
|
|
56
|
+
} | null;
|
|
57
|
+
isDrawing: boolean;
|
|
58
|
+
|
|
59
|
+
constructor(
|
|
60
|
+
toolProps: PublicToolProps = {},
|
|
61
|
+
defaultToolProps: ToolProps = {
|
|
62
|
+
supportedInteractionTypes: ['Mouse', 'Touch'],
|
|
63
|
+
configuration: {
|
|
64
|
+
shadow: true,
|
|
65
|
+
magnifyingGlass: {
|
|
66
|
+
radius: 125, // px
|
|
67
|
+
zoomFactor: 2.5,
|
|
68
|
+
zoomFactorList: [2.5, 3, 3.5, 4, 4.5, 5],
|
|
69
|
+
autoPan: {
|
|
70
|
+
enabled: true,
|
|
71
|
+
padding: 10, // px
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
actions: [
|
|
75
|
+
{
|
|
76
|
+
method: 'showZoomFactorsList',
|
|
77
|
+
bindings: [
|
|
78
|
+
{
|
|
79
|
+
mouseButton: MouseBindings.Secondary,
|
|
80
|
+
modifierKey: KeyboardBindings.Shift,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
}
|
|
87
|
+
) {
|
|
88
|
+
super(toolProps, defaultToolProps);
|
|
89
|
+
this.magnifyViewportManager = AdvancedMagnifyViewportManager.getInstance();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Based on the current position of the mouse and the current imageId to create
|
|
94
|
+
* a CircleROI Annotation and stores it in the annotationManager
|
|
95
|
+
*
|
|
96
|
+
* @param evt - EventTypes.NormalizedMouseEventType
|
|
97
|
+
* @returns The annotation object.
|
|
98
|
+
*
|
|
99
|
+
*/
|
|
100
|
+
addNewAnnotation = (
|
|
101
|
+
evt: EventTypes.InteractionEventType
|
|
102
|
+
): AdvancedMagnifyAnnotation => {
|
|
103
|
+
const eventDetail = evt.detail;
|
|
104
|
+
const { currentPoints, element } = eventDetail;
|
|
105
|
+
const enabledElement = getEnabledElement(element);
|
|
106
|
+
const { viewport, renderingEngine } = enabledElement;
|
|
107
|
+
const worldPos = currentPoints.world;
|
|
108
|
+
const canvasPos = currentPoints.canvas;
|
|
109
|
+
const { magnifyingGlass: config } = this.configuration;
|
|
110
|
+
const { radius, zoomFactor, autoPan } = config;
|
|
111
|
+
|
|
112
|
+
const worldHandlesPoints = this._getWorldHandlesPoints(
|
|
113
|
+
viewport,
|
|
114
|
+
canvasPos,
|
|
115
|
+
radius
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const camera = viewport.getCamera();
|
|
119
|
+
const { viewPlaneNormal, viewUp } = camera;
|
|
120
|
+
|
|
121
|
+
const referencedImageId = this.getReferencedImageId(
|
|
122
|
+
viewport,
|
|
123
|
+
worldPos,
|
|
124
|
+
viewPlaneNormal,
|
|
125
|
+
viewUp
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const annotationUID = csUtils.uuidv4();
|
|
129
|
+
const magnifyViewportId = csUtils.uuidv4();
|
|
130
|
+
const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
|
|
131
|
+
|
|
132
|
+
const annotation: AdvancedMagnifyAnnotation = {
|
|
133
|
+
annotationUID,
|
|
134
|
+
highlighted: true,
|
|
135
|
+
invalidated: true,
|
|
136
|
+
metadata: {
|
|
137
|
+
toolName: this.getToolName(),
|
|
138
|
+
viewPlaneNormal: <Types.Point3>[...viewPlaneNormal],
|
|
139
|
+
viewUp: <Types.Point3>[...viewUp],
|
|
140
|
+
FrameOfReferenceUID,
|
|
141
|
+
referencedImageId,
|
|
142
|
+
},
|
|
143
|
+
data: {
|
|
144
|
+
sourceViewportId: viewport.id,
|
|
145
|
+
magnifyViewportId,
|
|
146
|
+
zoomFactor,
|
|
147
|
+
handles: {
|
|
148
|
+
points: worldHandlesPoints,
|
|
149
|
+
activeHandleIndex: null,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
this.magnifyViewportManager.createViewport(annotation, {
|
|
155
|
+
magnifyViewportId,
|
|
156
|
+
sourceEnabledElement: enabledElement,
|
|
157
|
+
position: canvasPos,
|
|
158
|
+
radius,
|
|
159
|
+
zoomFactor,
|
|
160
|
+
autoPan: {
|
|
161
|
+
enabled: autoPan.enabled,
|
|
162
|
+
padding: autoPan.padding,
|
|
163
|
+
callback: (data: AutoPanCallbackData) => {
|
|
164
|
+
const annotationPoints = annotation.data.handles.points;
|
|
165
|
+
const { world: worldDelta } = data.delta;
|
|
166
|
+
|
|
167
|
+
for (let i = 0, len = annotationPoints.length; i < len; i++) {
|
|
168
|
+
annotationPoints[i][0] += worldDelta[0];
|
|
169
|
+
annotationPoints[i][1] += worldDelta[1];
|
|
170
|
+
annotationPoints[i][2] += worldDelta[2];
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
addAnnotation(annotation, element);
|
|
177
|
+
|
|
178
|
+
const viewportIdsToRender = getViewportIdsWithToolToRender(
|
|
179
|
+
element,
|
|
180
|
+
this.getToolName()
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
evt.preventDefault();
|
|
184
|
+
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
|
|
185
|
+
|
|
186
|
+
return annotation;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* It returns if the canvas point is near the provided annotation in the provided
|
|
191
|
+
* element or not. A proximity is passed to the function to determine the
|
|
192
|
+
* proximity of the point to the annotation in number of pixels.
|
|
193
|
+
*
|
|
194
|
+
* @param element - HTML Element
|
|
195
|
+
* @param annotation - Annotation
|
|
196
|
+
* @param canvasCoords - Canvas coordinates
|
|
197
|
+
* @param proximity - Proximity to tool to consider
|
|
198
|
+
* @returns Boolean, whether the canvas point is near tool
|
|
199
|
+
*/
|
|
200
|
+
public isPointNearTool = (
|
|
201
|
+
element: HTMLDivElement,
|
|
202
|
+
annotation: AdvancedMagnifyAnnotation,
|
|
203
|
+
canvasCoords: Types.Point2,
|
|
204
|
+
proximity: number
|
|
205
|
+
): boolean => {
|
|
206
|
+
const enabledElement = getEnabledElement(element);
|
|
207
|
+
const { viewport } = enabledElement;
|
|
208
|
+
|
|
209
|
+
const { data } = annotation;
|
|
210
|
+
const { points } = data.handles;
|
|
211
|
+
|
|
212
|
+
// For some reason Typescript doesn't understand this, so we need to be
|
|
213
|
+
// more specific about the type
|
|
214
|
+
const canvasCoordinates = points.map((p) => viewport.worldToCanvas(p)) as [
|
|
215
|
+
Types.Point2,
|
|
216
|
+
Types.Point2,
|
|
217
|
+
Types.Point2,
|
|
218
|
+
Types.Point2
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
const canvasTop = canvasCoordinates[0];
|
|
222
|
+
const canvasBottom = canvasCoordinates[2];
|
|
223
|
+
const canvasLeft = canvasCoordinates[3];
|
|
224
|
+
const radius = Math.abs(canvasBottom[1] - canvasTop[1]) * 0.5;
|
|
225
|
+
const center = [
|
|
226
|
+
canvasLeft[0] + radius,
|
|
227
|
+
canvasTop[1] + radius,
|
|
228
|
+
] as Types.Point2;
|
|
229
|
+
const radiusPoint = getCanvasCircleRadius([center, canvasCoords]);
|
|
230
|
+
|
|
231
|
+
if (Math.abs(radiusPoint - radius) < proximity * 1.5) {
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return false;
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
toolSelectedCallback = (
|
|
239
|
+
evt: EventTypes.InteractionEventType,
|
|
240
|
+
annotation: AdvancedMagnifyAnnotation
|
|
241
|
+
): void => {
|
|
242
|
+
const eventDetail = evt.detail;
|
|
243
|
+
const { element } = eventDetail;
|
|
244
|
+
|
|
245
|
+
annotation.highlighted = true;
|
|
246
|
+
|
|
247
|
+
const viewportIdsToRender = getViewportIdsWithToolToRender(
|
|
248
|
+
element,
|
|
249
|
+
this.getToolName()
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
this.editData = {
|
|
253
|
+
annotation,
|
|
254
|
+
viewportIdsToRender,
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
hideElementCursor(element);
|
|
258
|
+
|
|
259
|
+
this._activateModify(element);
|
|
260
|
+
|
|
261
|
+
const enabledElement = getEnabledElement(element);
|
|
262
|
+
const { renderingEngine } = enabledElement;
|
|
263
|
+
|
|
264
|
+
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
|
|
265
|
+
|
|
266
|
+
evt.preventDefault();
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
handleSelectedCallback = (
|
|
270
|
+
evt: EventTypes.InteractionEventType,
|
|
271
|
+
annotation: AdvancedMagnifyAnnotation,
|
|
272
|
+
handle: ToolHandle
|
|
273
|
+
): void => {
|
|
274
|
+
const eventDetail = evt.detail;
|
|
275
|
+
const { element } = eventDetail;
|
|
276
|
+
const { data } = annotation;
|
|
277
|
+
|
|
278
|
+
annotation.highlighted = true;
|
|
279
|
+
|
|
280
|
+
const { points } = data.handles;
|
|
281
|
+
const handleIndex = points.findIndex((p) => p === handle);
|
|
282
|
+
|
|
283
|
+
// Find viewports to render on drag.
|
|
284
|
+
const viewportIdsToRender = getViewportIdsWithToolToRender(
|
|
285
|
+
element,
|
|
286
|
+
this.getToolName()
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
this.editData = {
|
|
290
|
+
annotation,
|
|
291
|
+
viewportIdsToRender,
|
|
292
|
+
handleIndex,
|
|
293
|
+
};
|
|
294
|
+
this._activateModify(element);
|
|
295
|
+
|
|
296
|
+
hideElementCursor(element);
|
|
297
|
+
|
|
298
|
+
const enabledElement = getEnabledElement(element);
|
|
299
|
+
const { renderingEngine } = enabledElement;
|
|
300
|
+
|
|
301
|
+
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
|
|
302
|
+
|
|
303
|
+
evt.preventDefault();
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
_endCallback = (evt: EventTypes.InteractionEventType): void => {
|
|
307
|
+
const eventDetail = evt.detail;
|
|
308
|
+
const { element } = eventDetail;
|
|
309
|
+
|
|
310
|
+
const { annotation, viewportIdsToRender, newAnnotation } = this.editData;
|
|
311
|
+
const { data } = annotation;
|
|
312
|
+
|
|
313
|
+
data.handles.activeHandleIndex = null;
|
|
314
|
+
|
|
315
|
+
this._deactivateModify(element);
|
|
316
|
+
|
|
317
|
+
resetElementCursor(element);
|
|
318
|
+
|
|
319
|
+
const enabledElement = getEnabledElement(element);
|
|
320
|
+
const { renderingEngine } = enabledElement;
|
|
321
|
+
|
|
322
|
+
this.editData = null;
|
|
323
|
+
this.isDrawing = false;
|
|
324
|
+
|
|
325
|
+
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
|
|
326
|
+
|
|
327
|
+
if (newAnnotation) {
|
|
328
|
+
const eventType = Events.ANNOTATION_COMPLETED;
|
|
329
|
+
|
|
330
|
+
const eventDetail: AnnotationCompletedEventDetail = {
|
|
331
|
+
annotation,
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
triggerEvent(eventTarget, eventType, eventDetail);
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
_dragDrawCallback = (evt: EventTypes.InteractionEventType): void => {
|
|
339
|
+
this.isDrawing = true;
|
|
340
|
+
const eventDetail = evt.detail;
|
|
341
|
+
const { element, deltaPoints } = eventDetail;
|
|
342
|
+
const worldPosDelta = deltaPoints?.world ?? [0, 0, 0];
|
|
343
|
+
const enabledElement = getEnabledElement(element);
|
|
344
|
+
const { renderingEngine } = enabledElement;
|
|
345
|
+
|
|
346
|
+
const { annotation, viewportIdsToRender } = this.editData;
|
|
347
|
+
const { points } = annotation.data.handles;
|
|
348
|
+
|
|
349
|
+
points.forEach((point) => {
|
|
350
|
+
point[0] += worldPosDelta[0];
|
|
351
|
+
point[1] += worldPosDelta[1];
|
|
352
|
+
point[2] += worldPosDelta[2];
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
annotation.invalidated = true;
|
|
356
|
+
this.editData.hasMoved = true;
|
|
357
|
+
|
|
358
|
+
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
_dragModifyCallback = (evt: EventTypes.InteractionEventType): void => {
|
|
362
|
+
this.isDrawing = true;
|
|
363
|
+
const eventDetail = evt.detail;
|
|
364
|
+
const { element } = eventDetail;
|
|
365
|
+
|
|
366
|
+
const { annotation, viewportIdsToRender, handleIndex } = this.editData;
|
|
367
|
+
const { data } = annotation;
|
|
368
|
+
|
|
369
|
+
if (handleIndex === undefined) {
|
|
370
|
+
// Moving tool
|
|
371
|
+
const { deltaPoints } = eventDetail;
|
|
372
|
+
const worldPosDelta = deltaPoints.world;
|
|
373
|
+
|
|
374
|
+
const points = data.handles.points;
|
|
375
|
+
|
|
376
|
+
points.forEach((point) => {
|
|
377
|
+
point[0] += worldPosDelta[0];
|
|
378
|
+
point[1] += worldPosDelta[1];
|
|
379
|
+
point[2] += worldPosDelta[2];
|
|
380
|
+
});
|
|
381
|
+
annotation.invalidated = true;
|
|
382
|
+
} else {
|
|
383
|
+
this._dragHandle(evt);
|
|
384
|
+
annotation.invalidated = true;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const enabledElement = getEnabledElement(element);
|
|
388
|
+
const { renderingEngine } = enabledElement;
|
|
389
|
+
|
|
390
|
+
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
_dragHandle = (evt: EventTypes.InteractionEventType): void => {
|
|
394
|
+
const eventDetail = evt.detail;
|
|
395
|
+
const { element } = eventDetail;
|
|
396
|
+
const enabledElement = getEnabledElement(element);
|
|
397
|
+
const { viewport } = enabledElement;
|
|
398
|
+
const { worldToCanvas } = viewport;
|
|
399
|
+
|
|
400
|
+
const { annotation } = this.editData;
|
|
401
|
+
const { data } = annotation;
|
|
402
|
+
const { points } = data.handles;
|
|
403
|
+
|
|
404
|
+
const canvasCoordinates = points.map((p) => worldToCanvas(p));
|
|
405
|
+
const canvasTop = canvasCoordinates[0];
|
|
406
|
+
const canvasBottom = canvasCoordinates[2];
|
|
407
|
+
const canvasLeft = canvasCoordinates[3];
|
|
408
|
+
const radius = Math.abs(canvasBottom[1] - canvasTop[1]) * 0.5;
|
|
409
|
+
const canvasCenter: Types.Point2 = [
|
|
410
|
+
canvasLeft[0] + radius,
|
|
411
|
+
canvasTop[1] + radius,
|
|
412
|
+
];
|
|
413
|
+
|
|
414
|
+
const { currentPoints } = eventDetail;
|
|
415
|
+
const currentCanvasPoints = currentPoints.canvas;
|
|
416
|
+
|
|
417
|
+
const newRadius = getCanvasCircleRadius([
|
|
418
|
+
canvasCenter,
|
|
419
|
+
currentCanvasPoints,
|
|
420
|
+
]);
|
|
421
|
+
const newWorldHandlesPoints = this._getWorldHandlesPoints(
|
|
422
|
+
viewport,
|
|
423
|
+
canvasCenter,
|
|
424
|
+
newRadius
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
points[0] = newWorldHandlesPoints[0];
|
|
428
|
+
points[1] = newWorldHandlesPoints[1];
|
|
429
|
+
points[2] = newWorldHandlesPoints[2];
|
|
430
|
+
points[3] = newWorldHandlesPoints[3];
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
cancel = (element: HTMLDivElement) => {
|
|
434
|
+
// If it is mid-draw or mid-modify
|
|
435
|
+
if (!this.isDrawing) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
this.isDrawing = false;
|
|
440
|
+
this._deactivateModify(element);
|
|
441
|
+
resetElementCursor(element);
|
|
442
|
+
|
|
443
|
+
const { annotation, viewportIdsToRender, newAnnotation } = this.editData;
|
|
444
|
+
const { data } = annotation;
|
|
445
|
+
|
|
446
|
+
annotation.highlighted = false;
|
|
447
|
+
data.handles.activeHandleIndex = null;
|
|
448
|
+
|
|
449
|
+
const enabledElement = getEnabledElement(element);
|
|
450
|
+
const { renderingEngine } = enabledElement;
|
|
451
|
+
|
|
452
|
+
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
|
|
453
|
+
|
|
454
|
+
if (newAnnotation) {
|
|
455
|
+
const eventType = Events.ANNOTATION_COMPLETED;
|
|
456
|
+
|
|
457
|
+
const eventDetail: AnnotationCompletedEventDetail = {
|
|
458
|
+
annotation,
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
triggerEvent(eventTarget, eventType, eventDetail);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
this.editData = null;
|
|
465
|
+
return annotation.annotationUID;
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
_activateModify = (element) => {
|
|
469
|
+
state.isInteractingWithTool = true;
|
|
470
|
+
|
|
471
|
+
element.addEventListener(Events.MOUSE_UP, this._endCallback);
|
|
472
|
+
element.addEventListener(Events.MOUSE_DRAG, this._dragModifyCallback);
|
|
473
|
+
element.addEventListener(Events.MOUSE_CLICK, this._endCallback);
|
|
474
|
+
|
|
475
|
+
element.addEventListener(Events.TOUCH_END, this._endCallback);
|
|
476
|
+
element.addEventListener(Events.TOUCH_DRAG, this._dragModifyCallback);
|
|
477
|
+
element.addEventListener(Events.TOUCH_TAP, this._endCallback);
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
_deactivateModify = (element) => {
|
|
481
|
+
state.isInteractingWithTool = false;
|
|
482
|
+
|
|
483
|
+
element.removeEventListener(Events.MOUSE_UP, this._endCallback);
|
|
484
|
+
element.removeEventListener(Events.MOUSE_DRAG, this._dragModifyCallback);
|
|
485
|
+
element.removeEventListener(Events.MOUSE_CLICK, this._endCallback);
|
|
486
|
+
|
|
487
|
+
element.removeEventListener(Events.TOUCH_END, this._endCallback);
|
|
488
|
+
element.removeEventListener(Events.TOUCH_DRAG, this._dragModifyCallback);
|
|
489
|
+
element.removeEventListener(Events.TOUCH_TAP, this._endCallback);
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* it is used to draw the circleROI annotation in each
|
|
494
|
+
* request animation frame. It calculates the updated cached statistics if
|
|
495
|
+
* data is invalidated and cache it.
|
|
496
|
+
*
|
|
497
|
+
* @param enabledElement - The Cornerstone's enabledElement.
|
|
498
|
+
* @param svgDrawingHelper - The svgDrawingHelper providing the context for drawing.
|
|
499
|
+
*/
|
|
500
|
+
renderAnnotation = (
|
|
501
|
+
enabledElement: Types.IEnabledElement,
|
|
502
|
+
svgDrawingHelper: SVGDrawingHelper
|
|
503
|
+
): boolean => {
|
|
504
|
+
let renderStatus = false;
|
|
505
|
+
const { viewport } = enabledElement;
|
|
506
|
+
const { element } = viewport;
|
|
507
|
+
|
|
508
|
+
let annotations = getAnnotations(this.getToolName(), element);
|
|
509
|
+
|
|
510
|
+
if (!annotations?.length) {
|
|
511
|
+
return renderStatus;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
annotations = this.filterInteractableAnnotationsForElement(
|
|
515
|
+
element,
|
|
516
|
+
annotations
|
|
517
|
+
);
|
|
518
|
+
|
|
519
|
+
annotations = annotations?.filter(
|
|
520
|
+
(annotation) =>
|
|
521
|
+
(<AdvancedMagnifyAnnotation>annotation).data.sourceViewportId ===
|
|
522
|
+
viewport.id
|
|
523
|
+
);
|
|
524
|
+
|
|
525
|
+
if (!annotations?.length) {
|
|
526
|
+
return renderStatus;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const styleSpecifier: StyleSpecifier = {
|
|
530
|
+
toolGroupId: this.toolGroupId,
|
|
531
|
+
toolName: this.getToolName(),
|
|
532
|
+
viewportId: enabledElement.viewport.id,
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
for (let i = 0; i < annotations.length; i++) {
|
|
536
|
+
const annotation = annotations[i] as AdvancedMagnifyAnnotation;
|
|
537
|
+
const { annotationUID, data } = annotation;
|
|
538
|
+
const { magnifyViewportId, zoomFactor, handles } = data;
|
|
539
|
+
const { points, activeHandleIndex } = handles;
|
|
540
|
+
|
|
541
|
+
styleSpecifier.annotationUID = annotationUID;
|
|
542
|
+
|
|
543
|
+
const lineWidth = this.getStyle('lineWidth', styleSpecifier, annotation);
|
|
544
|
+
const lineDash = this.getStyle('lineDash', styleSpecifier, annotation);
|
|
545
|
+
const color = this.getStyle('color', styleSpecifier, annotation);
|
|
546
|
+
|
|
547
|
+
const canvasCoordinates = points.map((p) =>
|
|
548
|
+
viewport.worldToCanvas(p)
|
|
549
|
+
) as Types.Point2[];
|
|
550
|
+
const canvasTop = canvasCoordinates[0];
|
|
551
|
+
const canvasBottom = canvasCoordinates[2];
|
|
552
|
+
const canvasLeft = canvasCoordinates[3];
|
|
553
|
+
const radius = Math.abs(canvasBottom[1] - canvasTop[1]) * 0.5;
|
|
554
|
+
const center = [
|
|
555
|
+
canvasLeft[0] + radius,
|
|
556
|
+
canvasTop[1] + radius,
|
|
557
|
+
] as Types.Point2;
|
|
558
|
+
|
|
559
|
+
// If rendering engine has been destroyed while rendering
|
|
560
|
+
if (!viewport.getRenderingEngine()) {
|
|
561
|
+
console.warn('Rendering Engine has been destroyed');
|
|
562
|
+
return renderStatus;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
let activeHandleCanvasCoords;
|
|
566
|
+
|
|
567
|
+
if (!isAnnotationVisible(annotationUID)) {
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (
|
|
572
|
+
!isAnnotationLocked(annotation) &&
|
|
573
|
+
!this.editData &&
|
|
574
|
+
activeHandleIndex !== null
|
|
575
|
+
) {
|
|
576
|
+
// Not locked or creating and hovering over handle, so render handle.
|
|
577
|
+
activeHandleCanvasCoords = [canvasCoordinates[activeHandleIndex]];
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (activeHandleCanvasCoords) {
|
|
581
|
+
const handleGroupUID = '0';
|
|
582
|
+
drawHandlesSvg(
|
|
583
|
+
svgDrawingHelper,
|
|
584
|
+
annotationUID,
|
|
585
|
+
handleGroupUID,
|
|
586
|
+
activeHandleCanvasCoords,
|
|
587
|
+
{
|
|
588
|
+
color,
|
|
589
|
+
}
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
const dataId = `${annotationUID}-advancedMagnify`;
|
|
594
|
+
const circleUID = '0';
|
|
595
|
+
drawCircleSvg(
|
|
596
|
+
svgDrawingHelper,
|
|
597
|
+
annotationUID,
|
|
598
|
+
circleUID,
|
|
599
|
+
center,
|
|
600
|
+
radius,
|
|
601
|
+
{
|
|
602
|
+
color,
|
|
603
|
+
lineDash,
|
|
604
|
+
lineWidth,
|
|
605
|
+
},
|
|
606
|
+
dataId
|
|
607
|
+
);
|
|
608
|
+
|
|
609
|
+
const magnifyViewport =
|
|
610
|
+
this.magnifyViewportManager.getViewport(magnifyViewportId);
|
|
611
|
+
|
|
612
|
+
magnifyViewport.position = center;
|
|
613
|
+
magnifyViewport.radius = radius;
|
|
614
|
+
magnifyViewport.zoomFactor = zoomFactor;
|
|
615
|
+
magnifyViewport.update();
|
|
616
|
+
|
|
617
|
+
renderStatus = true;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
return renderStatus;
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
// Basic dropdown component that allows the user to select a different zoom factor.
|
|
624
|
+
// configurations.actions may be changed to use a customized dropdown.
|
|
625
|
+
public showZoomFactorsList(
|
|
626
|
+
evt: EventTypes.InteractionEventType,
|
|
627
|
+
annotation: AdvancedMagnifyAnnotation
|
|
628
|
+
) {
|
|
629
|
+
const { element, currentPoints } = evt.detail;
|
|
630
|
+
const enabledElement = getEnabledElement(element);
|
|
631
|
+
const { viewport } = enabledElement;
|
|
632
|
+
const { canvas: canvasPoint } = currentPoints;
|
|
633
|
+
const viewportElement = element.querySelector(':scope .viewport-element');
|
|
634
|
+
const currentZoomFactor = annotation.data.zoomFactor;
|
|
635
|
+
const remove = () => dropdown.parentElement.removeChild(dropdown);
|
|
636
|
+
|
|
637
|
+
const dropdown = this._getZoomFactorsListDropdown(
|
|
638
|
+
currentZoomFactor,
|
|
639
|
+
(newZoomFactor) => {
|
|
640
|
+
if (newZoomFactor !== undefined) {
|
|
641
|
+
annotation.data.zoomFactor = Number.parseFloat(newZoomFactor);
|
|
642
|
+
annotation.invalidated = true;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
remove();
|
|
646
|
+
viewport.render();
|
|
647
|
+
}
|
|
648
|
+
);
|
|
649
|
+
|
|
650
|
+
Object.assign(dropdown.style, {
|
|
651
|
+
left: `${canvasPoint[0]}px`,
|
|
652
|
+
top: `${canvasPoint[1]}px`,
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
viewportElement.appendChild(dropdown);
|
|
656
|
+
dropdown.focus();
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
private _getZoomFactorsListDropdown(currentZoomFactor, onChangeCallback) {
|
|
660
|
+
const { zoomFactorList } = this.configuration.magnifyingGlass;
|
|
661
|
+
const dropdown = document.createElement('select');
|
|
662
|
+
|
|
663
|
+
dropdown.size = 5;
|
|
664
|
+
Object.assign(dropdown.style, {
|
|
665
|
+
width: '50px',
|
|
666
|
+
position: 'absolute',
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
['mousedown', 'mouseup', 'mousemove', 'click'].forEach((eventName) => {
|
|
670
|
+
dropdown.addEventListener(eventName, (evt) => evt.stopPropagation());
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
dropdown.addEventListener('change', (evt) => {
|
|
674
|
+
evt.stopPropagation();
|
|
675
|
+
onChangeCallback(dropdown.value);
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
dropdown.addEventListener('keydown', (evt) => {
|
|
679
|
+
const shouldCancel =
|
|
680
|
+
(evt.keyCode ?? evt.which === 27) ||
|
|
681
|
+
evt.key?.toLowerCase() === 'escape';
|
|
682
|
+
|
|
683
|
+
if (shouldCancel) {
|
|
684
|
+
evt.stopPropagation();
|
|
685
|
+
onChangeCallback();
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
zoomFactorList.forEach((zoomFactor) => {
|
|
690
|
+
const option = document.createElement('option');
|
|
691
|
+
|
|
692
|
+
option.label = zoomFactor;
|
|
693
|
+
option.title = `Zoom factor ${zoomFactor.toFixed(1)}`;
|
|
694
|
+
option.value = zoomFactor;
|
|
695
|
+
option.defaultSelected = zoomFactor === currentZoomFactor;
|
|
696
|
+
|
|
697
|
+
dropdown.add(option);
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
return dropdown;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
private _getWorldHandlesPoints = (
|
|
704
|
+
viewport,
|
|
705
|
+
canvasCenterPos,
|
|
706
|
+
canvasRadius
|
|
707
|
+
): Types.Point3[] => {
|
|
708
|
+
const canvasHandlesPoints = [
|
|
709
|
+
[canvasCenterPos[0], canvasCenterPos[1] - canvasRadius], // top
|
|
710
|
+
[canvasCenterPos[0] + canvasRadius, canvasCenterPos[1]], // right
|
|
711
|
+
[canvasCenterPos[0], canvasCenterPos[1] + canvasRadius], // bottom
|
|
712
|
+
[canvasCenterPos[0] - canvasRadius, canvasCenterPos[1]], // left
|
|
713
|
+
];
|
|
714
|
+
|
|
715
|
+
const worldHandlesPoints = canvasHandlesPoints.map((p) =>
|
|
716
|
+
viewport.canvasToWorld(p)
|
|
717
|
+
) as Types.Point3[];
|
|
718
|
+
|
|
719
|
+
return worldHandlesPoints;
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
AdvancedMagnifyTool.toolName = 'AdvancedMagnify';
|
|
724
|
+
|
|
725
|
+
export { AdvancedMagnifyTool as default };
|