@cornerstonejs/tools 1.40.3 → 1.41.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/drawHandle.d.ts +4 -0
- package/dist/cjs/drawingSvg/drawHandle.js +66 -0
- package/dist/cjs/drawingSvg/drawHandle.js.map +1 -0
- package/dist/cjs/drawingSvg/drawHandles.js +4 -60
- package/dist/cjs/drawingSvg/drawHandles.js.map +1 -1
- package/dist/cjs/drawingSvg/index.d.ts +2 -1
- package/dist/cjs/drawingSvg/index.js +3 -1
- package/dist/cjs/drawingSvg/index.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/tools/annotation/AngleTool.js.map +1 -1
- package/dist/cjs/tools/annotation/LengthTool.js +4 -3
- package/dist/cjs/tools/annotation/LengthTool.js.map +1 -1
- package/dist/cjs/tools/annotation/ProbeTool.js +24 -3
- package/dist/cjs/tools/annotation/ProbeTool.js.map +1 -1
- package/dist/cjs/tools/annotation/UltrasoundDirectionalTool.d.ts +36 -0
- package/dist/cjs/tools/annotation/UltrasoundDirectionalTool.js +483 -0
- package/dist/cjs/tools/annotation/UltrasoundDirectionalTool.js.map +1 -0
- 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/ToolSpecificAnnotationTypes.d.ts +28 -0
- package/dist/cjs/utilities/getCalibratedUnits.d.ts +16 -2
- package/dist/cjs/utilities/getCalibratedUnits.js +127 -5
- package/dist/cjs/utilities/getCalibratedUnits.js.map +1 -1
- package/dist/esm/drawingSvg/drawHandle.js +61 -0
- package/dist/esm/drawingSvg/drawHandle.js.map +1 -0
- package/dist/esm/drawingSvg/drawHandles.js +4 -60
- package/dist/esm/drawingSvg/drawHandles.js.map +1 -1
- package/dist/esm/drawingSvg/index.js +2 -1
- package/dist/esm/drawingSvg/index.js.map +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/tools/annotation/AngleTool.js.map +1 -1
- package/dist/esm/tools/annotation/LengthTool.js +5 -4
- package/dist/esm/tools/annotation/LengthTool.js.map +1 -1
- package/dist/esm/tools/annotation/ProbeTool.js +24 -3
- package/dist/esm/tools/annotation/ProbeTool.js.map +1 -1
- package/dist/esm/tools/annotation/UltrasoundDirectionalTool.js +478 -0
- package/dist/esm/tools/annotation/UltrasoundDirectionalTool.js.map +1 -0
- package/dist/esm/tools/index.js +2 -1
- package/dist/esm/tools/index.js.map +1 -1
- package/dist/esm/utilities/getCalibratedUnits.js +125 -6
- package/dist/esm/utilities/getCalibratedUnits.js.map +1 -1
- package/dist/types/drawingSvg/drawHandle.d.ts +5 -0
- package/dist/types/drawingSvg/drawHandle.d.ts.map +1 -0
- package/dist/types/drawingSvg/drawHandles.d.ts.map +1 -1
- package/dist/types/drawingSvg/index.d.ts +2 -1
- package/dist/types/drawingSvg/index.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/tools/annotation/AngleTool.d.ts.map +1 -1
- package/dist/types/tools/annotation/LengthTool.d.ts.map +1 -1
- package/dist/types/tools/annotation/ProbeTool.d.ts.map +1 -1
- package/dist/types/tools/annotation/UltrasoundDirectionalTool.d.ts +37 -0
- package/dist/types/tools/annotation/UltrasoundDirectionalTool.d.ts.map +1 -0
- package/dist/types/tools/index.d.ts +2 -1
- package/dist/types/tools/index.d.ts.map +1 -1
- package/dist/types/types/ToolSpecificAnnotationTypes.d.ts +28 -0
- package/dist/types/types/ToolSpecificAnnotationTypes.d.ts.map +1 -1
- package/dist/types/utilities/getCalibratedUnits.d.ts +16 -2
- package/dist/types/utilities/getCalibratedUnits.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/drawHandle.ts +88 -0
- package/src/drawingSvg/drawHandles.ts +9 -75
- package/src/drawingSvg/index.ts +2 -0
- package/src/index.ts +2 -0
- package/src/tools/annotation/AngleTool.ts +0 -1
- package/src/tools/annotation/LengthTool.ts +6 -8
- package/src/tools/annotation/ProbeTool.ts +31 -7
- package/src/tools/annotation/UltrasoundDirectionalTool.ts +916 -0
- package/src/tools/index.ts +2 -0
- package/src/types/ToolSpecificAnnotationTypes.ts +29 -0
- package/src/utilities/getCalibratedUnits.ts +203 -7
|
@@ -0,0 +1,916 @@
|
|
|
1
|
+
import { Events } from '../../enums';
|
|
2
|
+
import {
|
|
3
|
+
getEnabledElement,
|
|
4
|
+
triggerEvent,
|
|
5
|
+
eventTarget,
|
|
6
|
+
utilities as csUtils,
|
|
7
|
+
StackViewport,
|
|
8
|
+
} from '@cornerstonejs/core';
|
|
9
|
+
import type { Types } from '@cornerstonejs/core';
|
|
10
|
+
|
|
11
|
+
import { AnnotationTool } from '../base';
|
|
12
|
+
import throttle from '../../utilities/throttle';
|
|
13
|
+
import {
|
|
14
|
+
addAnnotation,
|
|
15
|
+
getAnnotations,
|
|
16
|
+
removeAnnotation,
|
|
17
|
+
} from '../../stateManagement/annotation/annotationState';
|
|
18
|
+
import { UltrasoundDirectionalAnnotation } from '../../types/ToolSpecificAnnotationTypes';
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
drawHandle as drawHandleSvg,
|
|
22
|
+
drawLine as drawLineSvg,
|
|
23
|
+
drawLinkedTextBox as drawLinkedTextBoxSvg,
|
|
24
|
+
} from '../../drawingSvg';
|
|
25
|
+
import { state } from '../../store';
|
|
26
|
+
import { getViewportIdsWithToolToRender } from '../../utilities/viewportFilters';
|
|
27
|
+
import { roundNumber } from '../../utilities';
|
|
28
|
+
import { distanceToPoint } from '../../utilities/math/point';
|
|
29
|
+
import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds';
|
|
30
|
+
import {
|
|
31
|
+
AnnotationCompletedEventDetail,
|
|
32
|
+
AnnotationModifiedEventDetail,
|
|
33
|
+
} from '../../types/EventTypes';
|
|
34
|
+
|
|
35
|
+
import {
|
|
36
|
+
resetElementCursor,
|
|
37
|
+
hideElementCursor,
|
|
38
|
+
} from '../../cursors/elementCursor';
|
|
39
|
+
|
|
40
|
+
import {
|
|
41
|
+
EventTypes,
|
|
42
|
+
ToolHandle,
|
|
43
|
+
TextBoxHandle,
|
|
44
|
+
PublicToolProps,
|
|
45
|
+
ToolProps,
|
|
46
|
+
SVGDrawingHelper,
|
|
47
|
+
Annotation,
|
|
48
|
+
InteractionTypes,
|
|
49
|
+
} from '../../types';
|
|
50
|
+
import { StyleSpecifier } from '../../types/AnnotationStyle';
|
|
51
|
+
import { getCalibratedProbeUnitsAndValue } from '../../utilities/getCalibratedUnits';
|
|
52
|
+
const { transformWorldToIndex } = csUtils;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The `UltrasoundDirectionalTool` class is a tool for creating directional ultrasound annotations.
|
|
56
|
+
* It allows users to draw lines and measure distances between two points in the image.
|
|
57
|
+
* It automatically calculates the distance based on the relevant unit of measurement.
|
|
58
|
+
*/
|
|
59
|
+
class UltrasoundDirectionalTool extends AnnotationTool {
|
|
60
|
+
static toolName;
|
|
61
|
+
|
|
62
|
+
public touchDragCallback: any;
|
|
63
|
+
public mouseDragCallback: any;
|
|
64
|
+
startedDrawing: boolean;
|
|
65
|
+
_throttledCalculateCachedStats: any;
|
|
66
|
+
editData: {
|
|
67
|
+
annotation: any;
|
|
68
|
+
viewportIdsToRender: string[];
|
|
69
|
+
handleIndex?: number;
|
|
70
|
+
movingTextBox?: boolean;
|
|
71
|
+
newAnnotation?: boolean;
|
|
72
|
+
hasMoved?: boolean;
|
|
73
|
+
} | null;
|
|
74
|
+
isDrawing: boolean;
|
|
75
|
+
isHandleOutsideImage: boolean;
|
|
76
|
+
|
|
77
|
+
constructor(
|
|
78
|
+
toolProps: PublicToolProps = {},
|
|
79
|
+
defaultToolProps: ToolProps = {
|
|
80
|
+
supportedInteractionTypes: ['Mouse', 'Touch'],
|
|
81
|
+
configuration: {
|
|
82
|
+
shadow: true,
|
|
83
|
+
preventHandleOutsideImage: false,
|
|
84
|
+
getTextLines: defaultGetTextLines,
|
|
85
|
+
/**
|
|
86
|
+
* Determines whether both horizontal and vertical distances should be displayed
|
|
87
|
+
* in the text lines when generating annotations' measurement information.
|
|
88
|
+
*/
|
|
89
|
+
displayBothAxesDistances: false,
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
) {
|
|
93
|
+
super(toolProps, defaultToolProps);
|
|
94
|
+
|
|
95
|
+
this._throttledCalculateCachedStats = throttle(
|
|
96
|
+
this._calculateCachedStats,
|
|
97
|
+
100,
|
|
98
|
+
{ trailing: true }
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Based on the current position of the mouse and the current imageId to create
|
|
104
|
+
* a Ultrasound Directional Tool and store it in the annotationManager
|
|
105
|
+
*
|
|
106
|
+
* @param evt - EventTypes.InteractionEventType
|
|
107
|
+
* @returns The annotation object.
|
|
108
|
+
*/
|
|
109
|
+
addNewAnnotation = (
|
|
110
|
+
evt: EventTypes.InteractionEventType
|
|
111
|
+
): UltrasoundDirectionalAnnotation => {
|
|
112
|
+
if (this.startedDrawing) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
this.startedDrawing = true;
|
|
117
|
+
const eventDetail = evt.detail;
|
|
118
|
+
const { currentPoints, element } = eventDetail;
|
|
119
|
+
|
|
120
|
+
const worldPos = currentPoints.world;
|
|
121
|
+
const enabledElement = getEnabledElement(element);
|
|
122
|
+
const { viewport, renderingEngine } = enabledElement;
|
|
123
|
+
|
|
124
|
+
if (!(viewport instanceof StackViewport)) {
|
|
125
|
+
throw new Error(
|
|
126
|
+
'UltrasoundDirectionalTool can only be used on a StackViewport'
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
hideElementCursor(element);
|
|
131
|
+
this.isDrawing = true;
|
|
132
|
+
|
|
133
|
+
const camera = viewport.getCamera();
|
|
134
|
+
const { viewPlaneNormal, viewUp } = camera;
|
|
135
|
+
|
|
136
|
+
const referencedImageId = this.getReferencedImageId(
|
|
137
|
+
viewport,
|
|
138
|
+
worldPos,
|
|
139
|
+
viewPlaneNormal,
|
|
140
|
+
viewUp
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
|
|
144
|
+
|
|
145
|
+
const annotation = {
|
|
146
|
+
highlighted: true,
|
|
147
|
+
invalidated: true,
|
|
148
|
+
metadata: {
|
|
149
|
+
toolName: this.getToolName(),
|
|
150
|
+
viewPlaneNormal: <Types.Point3>[...viewPlaneNormal],
|
|
151
|
+
viewUp: <Types.Point3>[...viewUp],
|
|
152
|
+
FrameOfReferenceUID,
|
|
153
|
+
referencedImageId,
|
|
154
|
+
},
|
|
155
|
+
data: {
|
|
156
|
+
handles: {
|
|
157
|
+
points: [<Types.Point3>[...worldPos], <Types.Point3>[...worldPos]],
|
|
158
|
+
activeHandleIndex: null,
|
|
159
|
+
textBox: {
|
|
160
|
+
hasMoved: false,
|
|
161
|
+
worldPosition: <Types.Point3>[0, 0, 0],
|
|
162
|
+
worldBoundingBox: {
|
|
163
|
+
topLeft: <Types.Point3>[0, 0, 0],
|
|
164
|
+
topRight: <Types.Point3>[0, 0, 0],
|
|
165
|
+
bottomLeft: <Types.Point3>[0, 0, 0],
|
|
166
|
+
bottomRight: <Types.Point3>[0, 0, 0],
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
label: '',
|
|
171
|
+
cachedStats: {},
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
addAnnotation(annotation, element);
|
|
176
|
+
|
|
177
|
+
const viewportIdsToRender = getViewportIdsWithToolToRender(
|
|
178
|
+
element,
|
|
179
|
+
this.getToolName()
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
this.editData = {
|
|
183
|
+
annotation,
|
|
184
|
+
viewportIdsToRender,
|
|
185
|
+
handleIndex: 1,
|
|
186
|
+
movingTextBox: false,
|
|
187
|
+
newAnnotation: true,
|
|
188
|
+
hasMoved: false,
|
|
189
|
+
};
|
|
190
|
+
this._activateDraw(element);
|
|
191
|
+
|
|
192
|
+
evt.preventDefault();
|
|
193
|
+
|
|
194
|
+
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
|
|
195
|
+
|
|
196
|
+
return annotation;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* It returns if the canvas point is near the provided length annotation in the provided
|
|
201
|
+
* element or not. A proximity is passed to the function to determine the
|
|
202
|
+
* proximity of the point to the annotation in number of pixels.
|
|
203
|
+
*
|
|
204
|
+
* @param element - HTML Element
|
|
205
|
+
* @param annotation - Annotation
|
|
206
|
+
* @param canvasCoords - Canvas coordinates
|
|
207
|
+
* @param proximity - Proximity to tool to consider
|
|
208
|
+
* @returns Boolean, whether the canvas point is near tool
|
|
209
|
+
*/
|
|
210
|
+
isPointNearTool = (
|
|
211
|
+
element: HTMLDivElement,
|
|
212
|
+
annotation: UltrasoundDirectionalAnnotation,
|
|
213
|
+
canvasCoords: Types.Point2,
|
|
214
|
+
proximity: number
|
|
215
|
+
): boolean => {
|
|
216
|
+
return false;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
toolSelectedCallback(
|
|
220
|
+
evt: EventTypes.InteractionEventType,
|
|
221
|
+
annotation: Annotation,
|
|
222
|
+
interactionType: InteractionTypes,
|
|
223
|
+
canvasCoords?: Types.Point2
|
|
224
|
+
): void {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
handleSelectedCallback(
|
|
229
|
+
evt: EventTypes.InteractionEventType,
|
|
230
|
+
annotation: UltrasoundDirectionalAnnotation,
|
|
231
|
+
handle: ToolHandle
|
|
232
|
+
): void {
|
|
233
|
+
const eventDetail = evt.detail;
|
|
234
|
+
const { element } = eventDetail;
|
|
235
|
+
const { data } = annotation;
|
|
236
|
+
|
|
237
|
+
annotation.highlighted = true;
|
|
238
|
+
|
|
239
|
+
const viewportIdsToRender = getViewportIdsWithToolToRender(
|
|
240
|
+
element,
|
|
241
|
+
this.getToolName()
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
let movingTextBox = false;
|
|
245
|
+
let handleIndex;
|
|
246
|
+
if ((handle as TextBoxHandle).worldPosition) {
|
|
247
|
+
movingTextBox = true;
|
|
248
|
+
} else {
|
|
249
|
+
handleIndex = data.handles.points.findIndex((p) => p === handle);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Find viewports to render on drag.
|
|
253
|
+
|
|
254
|
+
this.editData = {
|
|
255
|
+
handleIndex,
|
|
256
|
+
annotation,
|
|
257
|
+
viewportIdsToRender,
|
|
258
|
+
};
|
|
259
|
+
this._activateModify(element);
|
|
260
|
+
|
|
261
|
+
hideElementCursor(element);
|
|
262
|
+
|
|
263
|
+
const enabledElement = getEnabledElement(element);
|
|
264
|
+
const { renderingEngine } = enabledElement;
|
|
265
|
+
|
|
266
|
+
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
|
|
267
|
+
|
|
268
|
+
evt.preventDefault();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
_endCallback = (evt: EventTypes.InteractionEventType): void => {
|
|
272
|
+
const eventDetail = evt.detail;
|
|
273
|
+
const { element } = eventDetail;
|
|
274
|
+
|
|
275
|
+
const { annotation, viewportIdsToRender, newAnnotation, hasMoved } =
|
|
276
|
+
this.editData;
|
|
277
|
+
|
|
278
|
+
const { data } = annotation;
|
|
279
|
+
if (newAnnotation && !hasMoved) {
|
|
280
|
+
// when user starts the drawing by click, and moving the mouse, instead
|
|
281
|
+
// of click and drag
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// If preventing new measurement means we are in the middle of an existing measurement
|
|
286
|
+
// we shouldn't deactivate modify or draw
|
|
287
|
+
if (this.startedDrawing && data.handles.points.length === 1) {
|
|
288
|
+
// adds the last point to the measurement
|
|
289
|
+
this.editData.handleIndex = 1;
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
this.startedDrawing = false;
|
|
294
|
+
data.handles.activeHandleIndex = null;
|
|
295
|
+
|
|
296
|
+
this._deactivateModify(element);
|
|
297
|
+
this._deactivateDraw(element);
|
|
298
|
+
resetElementCursor(element);
|
|
299
|
+
|
|
300
|
+
const enabledElement = getEnabledElement(element);
|
|
301
|
+
const { renderingEngine } = enabledElement;
|
|
302
|
+
|
|
303
|
+
if (
|
|
304
|
+
this.isHandleOutsideImage &&
|
|
305
|
+
this.configuration.preventHandleOutsideImage
|
|
306
|
+
) {
|
|
307
|
+
removeAnnotation(annotation.annotationUID);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
|
|
311
|
+
|
|
312
|
+
if (newAnnotation) {
|
|
313
|
+
const eventType = Events.ANNOTATION_COMPLETED;
|
|
314
|
+
|
|
315
|
+
const eventDetail: AnnotationCompletedEventDetail = {
|
|
316
|
+
annotation,
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
triggerEvent(eventTarget, eventType, eventDetail);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
this.editData = null;
|
|
323
|
+
this.isDrawing = false;
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
_dragCallback = (evt: EventTypes.InteractionEventType): void => {
|
|
327
|
+
this.isDrawing = true;
|
|
328
|
+
const eventDetail = evt.detail;
|
|
329
|
+
const { element } = eventDetail;
|
|
330
|
+
|
|
331
|
+
const { annotation, viewportIdsToRender, handleIndex, movingTextBox } =
|
|
332
|
+
this.editData;
|
|
333
|
+
const { data } = annotation;
|
|
334
|
+
|
|
335
|
+
if (movingTextBox) {
|
|
336
|
+
// Drag mode - moving text box
|
|
337
|
+
const { deltaPoints } = eventDetail as EventTypes.MouseDragEventDetail;
|
|
338
|
+
const worldPosDelta = deltaPoints.world;
|
|
339
|
+
|
|
340
|
+
const { textBox } = data.handles;
|
|
341
|
+
const { worldPosition } = textBox;
|
|
342
|
+
|
|
343
|
+
worldPosition[0] += worldPosDelta[0];
|
|
344
|
+
worldPosition[1] += worldPosDelta[1];
|
|
345
|
+
worldPosition[2] += worldPosDelta[2];
|
|
346
|
+
|
|
347
|
+
textBox.hasMoved = true;
|
|
348
|
+
} else if (handleIndex === undefined) {
|
|
349
|
+
// Drag mode - moving handle
|
|
350
|
+
const { deltaPoints } = eventDetail as EventTypes.MouseDragEventDetail;
|
|
351
|
+
const worldPosDelta = deltaPoints.world;
|
|
352
|
+
|
|
353
|
+
const points = data.handles.points;
|
|
354
|
+
|
|
355
|
+
points.forEach((point) => {
|
|
356
|
+
point[0] += worldPosDelta[0];
|
|
357
|
+
point[1] += worldPosDelta[1];
|
|
358
|
+
point[2] += worldPosDelta[2];
|
|
359
|
+
});
|
|
360
|
+
annotation.invalidated = true;
|
|
361
|
+
} else {
|
|
362
|
+
// Move mode - after double click, and mouse move to draw
|
|
363
|
+
const { currentPoints } = eventDetail;
|
|
364
|
+
const worldPos = currentPoints.world;
|
|
365
|
+
|
|
366
|
+
data.handles.points[handleIndex] = [...worldPos];
|
|
367
|
+
annotation.invalidated = true;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
this.editData.hasMoved = true;
|
|
371
|
+
|
|
372
|
+
const enabledElement = getEnabledElement(element);
|
|
373
|
+
const { renderingEngine } = enabledElement;
|
|
374
|
+
|
|
375
|
+
triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender);
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
cancel = (element: HTMLDivElement) => {
|
|
379
|
+
// If it is mid-draw or mid-modify
|
|
380
|
+
if (this.isDrawing) {
|
|
381
|
+
this.isDrawing = false;
|
|
382
|
+
this._deactivateDraw(element);
|
|
383
|
+
this._deactivateModify(element);
|
|
384
|
+
resetElementCursor(element);
|
|
385
|
+
|
|
386
|
+
const { annotation, viewportIdsToRender, newAnnotation } = this.editData;
|
|
387
|
+
const { data } = annotation;
|
|
388
|
+
|
|
389
|
+
annotation.highlighted = false;
|
|
390
|
+
data.handles.activeHandleIndex = null;
|
|
391
|
+
|
|
392
|
+
const enabledElement = getEnabledElement(element);
|
|
393
|
+
const { renderingEngine } = enabledElement;
|
|
394
|
+
|
|
395
|
+
triggerAnnotationRenderForViewportIds(
|
|
396
|
+
renderingEngine,
|
|
397
|
+
viewportIdsToRender
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
if (newAnnotation) {
|
|
401
|
+
const eventType = Events.ANNOTATION_COMPLETED;
|
|
402
|
+
|
|
403
|
+
const eventDetail: AnnotationCompletedEventDetail = {
|
|
404
|
+
annotation,
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
triggerEvent(eventTarget, eventType, eventDetail);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
this.editData = null;
|
|
411
|
+
this.startedDrawing = false;
|
|
412
|
+
return annotation.annotationUID;
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
_activateModify = (element: HTMLDivElement) => {
|
|
417
|
+
state.isInteractingWithTool = true;
|
|
418
|
+
|
|
419
|
+
element.addEventListener(
|
|
420
|
+
Events.MOUSE_UP,
|
|
421
|
+
this._endCallback as EventListener
|
|
422
|
+
);
|
|
423
|
+
element.addEventListener(
|
|
424
|
+
Events.MOUSE_DRAG,
|
|
425
|
+
this._dragCallback as EventListener
|
|
426
|
+
);
|
|
427
|
+
element.addEventListener(
|
|
428
|
+
Events.MOUSE_CLICK,
|
|
429
|
+
this._endCallback as EventListener
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
element.addEventListener(
|
|
433
|
+
Events.TOUCH_TAP,
|
|
434
|
+
this._endCallback as EventListener
|
|
435
|
+
);
|
|
436
|
+
element.addEventListener(
|
|
437
|
+
Events.TOUCH_END,
|
|
438
|
+
this._endCallback as EventListener
|
|
439
|
+
);
|
|
440
|
+
element.addEventListener(
|
|
441
|
+
Events.TOUCH_DRAG,
|
|
442
|
+
this._dragCallback as EventListener
|
|
443
|
+
);
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
_deactivateModify = (element: HTMLDivElement) => {
|
|
447
|
+
state.isInteractingWithTool = false;
|
|
448
|
+
|
|
449
|
+
element.removeEventListener(
|
|
450
|
+
Events.MOUSE_UP,
|
|
451
|
+
this._endCallback as EventListener
|
|
452
|
+
);
|
|
453
|
+
element.removeEventListener(
|
|
454
|
+
Events.MOUSE_DRAG,
|
|
455
|
+
this._dragCallback as EventListener
|
|
456
|
+
);
|
|
457
|
+
element.removeEventListener(
|
|
458
|
+
Events.MOUSE_CLICK,
|
|
459
|
+
this._endCallback as EventListener
|
|
460
|
+
);
|
|
461
|
+
element.removeEventListener(
|
|
462
|
+
Events.TOUCH_TAP,
|
|
463
|
+
this._endCallback as EventListener
|
|
464
|
+
);
|
|
465
|
+
element.removeEventListener(
|
|
466
|
+
Events.TOUCH_END,
|
|
467
|
+
this._endCallback as EventListener
|
|
468
|
+
);
|
|
469
|
+
element.removeEventListener(
|
|
470
|
+
Events.TOUCH_DRAG,
|
|
471
|
+
this._dragCallback as EventListener
|
|
472
|
+
);
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
_activateDraw = (element: HTMLDivElement) => {
|
|
476
|
+
state.isInteractingWithTool = true;
|
|
477
|
+
|
|
478
|
+
element.addEventListener(
|
|
479
|
+
Events.MOUSE_UP,
|
|
480
|
+
this._endCallback as EventListener
|
|
481
|
+
);
|
|
482
|
+
element.addEventListener(
|
|
483
|
+
Events.MOUSE_DRAG,
|
|
484
|
+
this._dragCallback as EventListener
|
|
485
|
+
);
|
|
486
|
+
element.addEventListener(
|
|
487
|
+
Events.MOUSE_MOVE,
|
|
488
|
+
this._dragCallback as EventListener
|
|
489
|
+
);
|
|
490
|
+
element.addEventListener(
|
|
491
|
+
Events.MOUSE_CLICK,
|
|
492
|
+
this._endCallback as EventListener
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
element.addEventListener(
|
|
496
|
+
Events.TOUCH_TAP,
|
|
497
|
+
this._endCallback as EventListener
|
|
498
|
+
);
|
|
499
|
+
element.addEventListener(
|
|
500
|
+
Events.TOUCH_END,
|
|
501
|
+
this._endCallback as EventListener
|
|
502
|
+
);
|
|
503
|
+
element.addEventListener(
|
|
504
|
+
Events.TOUCH_DRAG,
|
|
505
|
+
this._dragCallback as EventListener
|
|
506
|
+
);
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
_deactivateDraw = (element: HTMLDivElement) => {
|
|
510
|
+
state.isInteractingWithTool = false;
|
|
511
|
+
|
|
512
|
+
element.removeEventListener(
|
|
513
|
+
Events.MOUSE_UP,
|
|
514
|
+
this._endCallback as EventListener
|
|
515
|
+
);
|
|
516
|
+
element.removeEventListener(
|
|
517
|
+
Events.MOUSE_DRAG,
|
|
518
|
+
this._dragCallback as EventListener
|
|
519
|
+
);
|
|
520
|
+
element.removeEventListener(
|
|
521
|
+
Events.MOUSE_MOVE,
|
|
522
|
+
this._dragCallback as EventListener
|
|
523
|
+
);
|
|
524
|
+
element.removeEventListener(
|
|
525
|
+
Events.MOUSE_CLICK,
|
|
526
|
+
this._endCallback as EventListener
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
element.removeEventListener(
|
|
530
|
+
Events.TOUCH_TAP,
|
|
531
|
+
this._endCallback as EventListener
|
|
532
|
+
);
|
|
533
|
+
element.removeEventListener(
|
|
534
|
+
Events.TOUCH_END,
|
|
535
|
+
this._endCallback as EventListener
|
|
536
|
+
);
|
|
537
|
+
element.removeEventListener(
|
|
538
|
+
Events.TOUCH_DRAG,
|
|
539
|
+
this._dragCallback as EventListener
|
|
540
|
+
);
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* it is used to draw the length annotation in each
|
|
545
|
+
* request animation frame. It calculates the updated cached statistics if
|
|
546
|
+
* data is invalidated and cache it.
|
|
547
|
+
*
|
|
548
|
+
* @param enabledElement - The Cornerstone's enabledElement.
|
|
549
|
+
* @param svgDrawingHelper - The svgDrawingHelper providing the context for drawing.
|
|
550
|
+
*/
|
|
551
|
+
renderAnnotation = (
|
|
552
|
+
enabledElement: Types.IEnabledElement,
|
|
553
|
+
svgDrawingHelper: SVGDrawingHelper
|
|
554
|
+
): boolean => {
|
|
555
|
+
let renderStatus = false;
|
|
556
|
+
|
|
557
|
+
const { viewport } = enabledElement;
|
|
558
|
+
const { element } = viewport;
|
|
559
|
+
|
|
560
|
+
let annotations = getAnnotations(this.getToolName(), element);
|
|
561
|
+
|
|
562
|
+
// Todo: We don't need this anymore, filtering happens in triggerAnnotationRender
|
|
563
|
+
if (!annotations?.length) {
|
|
564
|
+
return renderStatus;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
annotations = this.filterInteractableAnnotationsForElement(
|
|
568
|
+
element,
|
|
569
|
+
annotations
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
if (!annotations?.length) {
|
|
573
|
+
return renderStatus;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const targetId = this.getTargetId(viewport);
|
|
577
|
+
const renderingEngine = viewport.getRenderingEngine();
|
|
578
|
+
|
|
579
|
+
const styleSpecifier: StyleSpecifier = {
|
|
580
|
+
toolGroupId: this.toolGroupId,
|
|
581
|
+
toolName: this.getToolName(),
|
|
582
|
+
viewportId: enabledElement.viewport.id,
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
// Draw SVG
|
|
586
|
+
for (let i = 0; i < annotations.length; i++) {
|
|
587
|
+
const annotation = annotations[i] as UltrasoundDirectionalAnnotation;
|
|
588
|
+
const { annotationUID, data } = annotation;
|
|
589
|
+
const { points } = data.handles;
|
|
590
|
+
|
|
591
|
+
styleSpecifier.annotationUID = annotationUID;
|
|
592
|
+
|
|
593
|
+
const color = this.getStyle('color', styleSpecifier, annotation);
|
|
594
|
+
|
|
595
|
+
const canvasCoordinates = points.map((p) => viewport.worldToCanvas(p));
|
|
596
|
+
|
|
597
|
+
// WE HAVE TO CACHE STATS BEFORE FETCHING TEXT
|
|
598
|
+
if (
|
|
599
|
+
!data.cachedStats[targetId] ||
|
|
600
|
+
data.cachedStats[targetId].xValues == null
|
|
601
|
+
) {
|
|
602
|
+
data.cachedStats[targetId] = {
|
|
603
|
+
xValues: [0, 0],
|
|
604
|
+
yValues: [0, 0],
|
|
605
|
+
isHorizontal: false,
|
|
606
|
+
units: [''],
|
|
607
|
+
isUnitless: false,
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
this._calculateCachedStats(annotation, renderingEngine, enabledElement);
|
|
611
|
+
} else if (annotation.invalidated) {
|
|
612
|
+
this._throttledCalculateCachedStats(
|
|
613
|
+
annotation,
|
|
614
|
+
renderingEngine,
|
|
615
|
+
enabledElement
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// If rendering engine has been destroyed while rendering
|
|
620
|
+
if (!viewport.getRenderingEngine()) {
|
|
621
|
+
console.warn('Rendering Engine has been destroyed');
|
|
622
|
+
return renderStatus;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// draw first point
|
|
626
|
+
let handleGroupUID = '0';
|
|
627
|
+
drawHandleSvg(
|
|
628
|
+
svgDrawingHelper,
|
|
629
|
+
annotationUID,
|
|
630
|
+
handleGroupUID,
|
|
631
|
+
canvasCoordinates[0],
|
|
632
|
+
{
|
|
633
|
+
color,
|
|
634
|
+
},
|
|
635
|
+
0
|
|
636
|
+
);
|
|
637
|
+
|
|
638
|
+
renderStatus = true;
|
|
639
|
+
|
|
640
|
+
if (canvasCoordinates.length !== 2) {
|
|
641
|
+
return renderStatus;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
handleGroupUID = '1';
|
|
645
|
+
drawHandleSvg(
|
|
646
|
+
svgDrawingHelper,
|
|
647
|
+
annotationUID,
|
|
648
|
+
handleGroupUID,
|
|
649
|
+
canvasCoordinates[1],
|
|
650
|
+
{
|
|
651
|
+
color,
|
|
652
|
+
},
|
|
653
|
+
1
|
|
654
|
+
);
|
|
655
|
+
|
|
656
|
+
const isUnitless = data.cachedStats[targetId].isUnitless;
|
|
657
|
+
|
|
658
|
+
if (!isUnitless) {
|
|
659
|
+
const canvasPoint1 = canvasCoordinates[0];
|
|
660
|
+
const canvasPoint2 = canvasCoordinates[1];
|
|
661
|
+
|
|
662
|
+
const canvasDeltaY = canvasPoint2[1] - canvasPoint1[1];
|
|
663
|
+
const canvasDeltaX = canvasPoint2[0] - canvasPoint1[0];
|
|
664
|
+
|
|
665
|
+
const isHorizontal = data.cachedStats[targetId].isHorizontal;
|
|
666
|
+
|
|
667
|
+
// then for the third point we need to go from first point towards
|
|
668
|
+
// the second point (it can be left or right in the horizontal orientation)
|
|
669
|
+
// or up or down in the vertical orientation, and only add
|
|
670
|
+
// the delta y to the x or y coordinate of the first point
|
|
671
|
+
let projectedPointCanvas = [0, 0] as Types.Point2;
|
|
672
|
+
if (isHorizontal) {
|
|
673
|
+
projectedPointCanvas = [
|
|
674
|
+
canvasPoint1[0] + canvasDeltaX,
|
|
675
|
+
canvasPoint1[1],
|
|
676
|
+
];
|
|
677
|
+
} else {
|
|
678
|
+
projectedPointCanvas = [
|
|
679
|
+
canvasPoint1[0],
|
|
680
|
+
canvasPoint1[1] + canvasDeltaY,
|
|
681
|
+
];
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// create a line from the first point to the third point
|
|
685
|
+
let dataId = `${annotationUID}-line-1`;
|
|
686
|
+
let lineUID = '1';
|
|
687
|
+
drawLineSvg(
|
|
688
|
+
svgDrawingHelper,
|
|
689
|
+
annotationUID,
|
|
690
|
+
lineUID,
|
|
691
|
+
canvasCoordinates[0],
|
|
692
|
+
projectedPointCanvas,
|
|
693
|
+
{
|
|
694
|
+
color,
|
|
695
|
+
width: 1,
|
|
696
|
+
shadow: this.configuration.shadow,
|
|
697
|
+
},
|
|
698
|
+
dataId
|
|
699
|
+
);
|
|
700
|
+
|
|
701
|
+
// draw another line from first point to the projected one
|
|
702
|
+
dataId = `${annotationUID}-line-2`;
|
|
703
|
+
lineUID = '2';
|
|
704
|
+
|
|
705
|
+
drawLineSvg(
|
|
706
|
+
svgDrawingHelper,
|
|
707
|
+
annotationUID,
|
|
708
|
+
lineUID,
|
|
709
|
+
canvasCoordinates[1],
|
|
710
|
+
projectedPointCanvas,
|
|
711
|
+
{
|
|
712
|
+
color,
|
|
713
|
+
width: 1,
|
|
714
|
+
lineDash: [1, 1],
|
|
715
|
+
shadow: this.configuration.shadow,
|
|
716
|
+
},
|
|
717
|
+
dataId
|
|
718
|
+
);
|
|
719
|
+
} else {
|
|
720
|
+
// draw straight line between the two points
|
|
721
|
+
const dataId = `${annotationUID}-line-1`;
|
|
722
|
+
const lineUID = '1';
|
|
723
|
+
drawLineSvg(
|
|
724
|
+
svgDrawingHelper,
|
|
725
|
+
annotationUID,
|
|
726
|
+
lineUID,
|
|
727
|
+
canvasCoordinates[0],
|
|
728
|
+
canvasCoordinates[1],
|
|
729
|
+
{
|
|
730
|
+
color,
|
|
731
|
+
width: 1,
|
|
732
|
+
shadow: this.configuration.shadow,
|
|
733
|
+
},
|
|
734
|
+
dataId
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// draw another line from first point to the
|
|
739
|
+
|
|
740
|
+
const options = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
|
|
741
|
+
if (!options.visibility) {
|
|
742
|
+
data.handles.textBox = {
|
|
743
|
+
hasMoved: false,
|
|
744
|
+
worldPosition: <Types.Point3>[0, 0, 0],
|
|
745
|
+
worldBoundingBox: {
|
|
746
|
+
topLeft: <Types.Point3>[0, 0, 0],
|
|
747
|
+
topRight: <Types.Point3>[0, 0, 0],
|
|
748
|
+
bottomLeft: <Types.Point3>[0, 0, 0],
|
|
749
|
+
bottomRight: <Types.Point3>[0, 0, 0],
|
|
750
|
+
},
|
|
751
|
+
};
|
|
752
|
+
continue;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
const textLines = this.configuration.getTextLines(
|
|
756
|
+
data,
|
|
757
|
+
targetId,
|
|
758
|
+
this.configuration
|
|
759
|
+
);
|
|
760
|
+
|
|
761
|
+
if (!data.handles.textBox.hasMoved) {
|
|
762
|
+
// linked to the vertex by default
|
|
763
|
+
const canvasTextBoxCoords = canvasCoordinates[1];
|
|
764
|
+
|
|
765
|
+
data.handles.textBox.worldPosition =
|
|
766
|
+
viewport.canvasToWorld(canvasTextBoxCoords);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
const textBoxPosition = viewport.worldToCanvas(
|
|
770
|
+
data.handles.textBox.worldPosition
|
|
771
|
+
);
|
|
772
|
+
|
|
773
|
+
const textBoxUID = '1';
|
|
774
|
+
const boundingBox = drawLinkedTextBoxSvg(
|
|
775
|
+
svgDrawingHelper,
|
|
776
|
+
annotationUID,
|
|
777
|
+
textBoxUID,
|
|
778
|
+
textLines,
|
|
779
|
+
textBoxPosition,
|
|
780
|
+
canvasCoordinates,
|
|
781
|
+
{},
|
|
782
|
+
options
|
|
783
|
+
);
|
|
784
|
+
|
|
785
|
+
const { x: left, y: top, width, height } = boundingBox;
|
|
786
|
+
|
|
787
|
+
data.handles.textBox.worldBoundingBox = {
|
|
788
|
+
topLeft: viewport.canvasToWorld([left, top]),
|
|
789
|
+
topRight: viewport.canvasToWorld([left + width, top]),
|
|
790
|
+
bottomLeft: viewport.canvasToWorld([left, top + height]),
|
|
791
|
+
bottomRight: viewport.canvasToWorld([left + width, top + height]),
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
return renderStatus;
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
_calculateCachedStats(annotation, renderingEngine, enabledElement) {
|
|
799
|
+
const data = annotation.data;
|
|
800
|
+
const { viewportId, renderingEngineId } = enabledElement;
|
|
801
|
+
|
|
802
|
+
// Until we have all two anchors bail out
|
|
803
|
+
if (data.handles.points.length !== 2) {
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
const { cachedStats } = data;
|
|
808
|
+
const targetIds = Object.keys(cachedStats);
|
|
809
|
+
|
|
810
|
+
for (let i = 0; i < targetIds.length; i++) {
|
|
811
|
+
const targetId = targetIds[i];
|
|
812
|
+
|
|
813
|
+
const image = this.getTargetIdImage(targetId, renderingEngine);
|
|
814
|
+
|
|
815
|
+
// If image does not exists for the targetId, skip. This can be due
|
|
816
|
+
// to various reasons such as if the target was a volumeViewport, and
|
|
817
|
+
// the volumeViewport has been decached in the meantime.
|
|
818
|
+
if (!image) {
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const { imageData } = image;
|
|
823
|
+
|
|
824
|
+
const worldPos1 = data.handles.points[0];
|
|
825
|
+
const worldPos2 = data.handles.points[1];
|
|
826
|
+
|
|
827
|
+
const imageIndex1 = transformWorldToIndex(imageData, worldPos1);
|
|
828
|
+
const imageIndex2 = transformWorldToIndex(imageData, worldPos2);
|
|
829
|
+
|
|
830
|
+
const { values: values1, units: units1 } =
|
|
831
|
+
getCalibratedProbeUnitsAndValue(image, [imageIndex1]);
|
|
832
|
+
const { values: values2, units: units2 } =
|
|
833
|
+
getCalibratedProbeUnitsAndValue(image, [imageIndex2]);
|
|
834
|
+
|
|
835
|
+
let xValues, yValues, units, isHorizontal;
|
|
836
|
+
let isUnitless = false;
|
|
837
|
+
if (
|
|
838
|
+
units1[0] !== units2[0] ||
|
|
839
|
+
units1[1] !== units2[1] ||
|
|
840
|
+
(units1[0] === 'raw' && units2[0] === 'raw')
|
|
841
|
+
) {
|
|
842
|
+
// if units are not the same, we cannot calculate the diff
|
|
843
|
+
// so we just report the px distance
|
|
844
|
+
const value = distanceToPoint(worldPos1, worldPos2);
|
|
845
|
+
|
|
846
|
+
xValues = [value, 0];
|
|
847
|
+
yValues = [value, 0];
|
|
848
|
+
units = ['px'];
|
|
849
|
+
isUnitless = true;
|
|
850
|
+
} else {
|
|
851
|
+
const canvasPoint1 = enabledElement.viewport.worldToCanvas(worldPos1);
|
|
852
|
+
const canvasPoint2 = enabledElement.viewport.worldToCanvas(worldPos2);
|
|
853
|
+
|
|
854
|
+
const canvasDeltaY = canvasPoint2[1] - canvasPoint1[1];
|
|
855
|
+
const canvasDeltaX = canvasPoint2[0] - canvasPoint1[0];
|
|
856
|
+
|
|
857
|
+
isHorizontal = Math.abs(canvasDeltaX) > Math.abs(canvasDeltaY);
|
|
858
|
+
xValues = [values1[0], values2[0]];
|
|
859
|
+
yValues = [values1[1], values2[1]];
|
|
860
|
+
|
|
861
|
+
units = [units1[0], units1[1]];
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
cachedStats[targetId] = {
|
|
865
|
+
xValues,
|
|
866
|
+
yValues,
|
|
867
|
+
isHorizontal,
|
|
868
|
+
units,
|
|
869
|
+
isUnitless,
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
annotation.invalidated = false;
|
|
874
|
+
|
|
875
|
+
// Dispatching annotation modified
|
|
876
|
+
const eventType = Events.ANNOTATION_MODIFIED;
|
|
877
|
+
|
|
878
|
+
const eventDetail: AnnotationModifiedEventDetail = {
|
|
879
|
+
annotation,
|
|
880
|
+
viewportId,
|
|
881
|
+
renderingEngineId,
|
|
882
|
+
};
|
|
883
|
+
triggerEvent(eventTarget, eventType, eventDetail);
|
|
884
|
+
|
|
885
|
+
return cachedStats;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
function defaultGetTextLines(data, targetId, configuration): string[] {
|
|
890
|
+
const cachedStats = data.cachedStats[targetId];
|
|
891
|
+
const { xValues, yValues, units, isUnitless, isHorizontal } = cachedStats;
|
|
892
|
+
|
|
893
|
+
if (isUnitless) {
|
|
894
|
+
return [`${roundNumber(xValues[0])} px`];
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
if (configuration.displayBothAxesDistances) {
|
|
898
|
+
const dist1 = Math.abs(xValues[1] - xValues[0]);
|
|
899
|
+
const dist2 = Math.abs(yValues[1] - yValues[0]);
|
|
900
|
+
return [
|
|
901
|
+
`${roundNumber(dist1)} ${units[0]}`,
|
|
902
|
+
`${roundNumber(dist2)} ${units[1]}`,
|
|
903
|
+
];
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
if (isHorizontal) {
|
|
907
|
+
const dist = Math.abs(xValues[1] - xValues[0]);
|
|
908
|
+
return [`${roundNumber(dist)} ${units[0]}`];
|
|
909
|
+
} else {
|
|
910
|
+
const dist = Math.abs(yValues[1] - yValues[0]);
|
|
911
|
+
return [`${roundNumber(dist)} ${units[1]}`];
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
UltrasoundDirectionalTool.toolName = 'UltrasoundDirectionalTool';
|
|
916
|
+
export default UltrasoundDirectionalTool;
|