larvitar 2.0.4 → 2.0.6
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/README.md +2 -2
- package/dist/larvitar.js +5 -3
- package/dist/larvitar.js.map +1 -1
- package/package.json +6 -2
- package/.github/workflows/build-docs.yml +0 -59
- package/.github/workflows/codeql-analysis.yml +0 -71
- package/.github/workflows/deploy.yml +0 -37
- package/.vscode/settings.json +0 -4
- package/CODE_OF_CONDUCT.md +0 -76
- package/MIGRATION.md +0 -25
- package/bundler/webpack.common.js +0 -27
- package/bundler/webpack.dev.js +0 -23
- package/bundler/webpack.prod.js +0 -19
- package/decs.d.ts +0 -12
- package/imaging/MetaDataReadable.ts +0 -42
- package/imaging/MetaDataTypes.ts +0 -3491
- package/imaging/dataDictionary.json +0 -21866
- package/imaging/imageAnonymization.ts +0 -135
- package/imaging/imageColormaps.ts +0 -217
- package/imaging/imageContours.ts +0 -196
- package/imaging/imageIo.ts +0 -251
- package/imaging/imageLayers.ts +0 -121
- package/imaging/imageLoading.ts +0 -299
- package/imaging/imageParsing.ts +0 -444
- package/imaging/imagePresets.ts +0 -156
- package/imaging/imageRendering.ts +0 -1091
- package/imaging/imageReslice.ts +0 -87
- package/imaging/imageStore.ts +0 -487
- package/imaging/imageTags.ts +0 -609
- package/imaging/imageTools.js +0 -708
- package/imaging/imageUtils.ts +0 -1079
- package/imaging/loaders/commonLoader.ts +0 -275
- package/imaging/loaders/dicomLoader.ts +0 -66
- package/imaging/loaders/fileLoader.ts +0 -71
- package/imaging/loaders/multiframeLoader.ts +0 -435
- package/imaging/loaders/nrrdLoader.ts +0 -630
- package/imaging/loaders/resliceLoader.ts +0 -205
- package/imaging/monitors/memory.ts +0 -151
- package/imaging/monitors/performance.ts +0 -34
- package/imaging/parsers/ecg.ts +0 -54
- package/imaging/parsers/nrrd.js +0 -485
- package/imaging/tools/README.md +0 -27
- package/imaging/tools/custom/4dSliceScrollTool.js +0 -146
- package/imaging/tools/custom/BorderMagnifyTool.js +0 -99
- package/imaging/tools/custom/contourTool.js +0 -1884
- package/imaging/tools/custom/diameterTool.js +0 -141
- package/imaging/tools/custom/editMaskTool.js +0 -141
- package/imaging/tools/custom/ellipticalRoiOverlayTool.js +0 -534
- package/imaging/tools/custom/polygonSegmentationMixin.js +0 -245
- package/imaging/tools/custom/polylineScissorsTool.js +0 -59
- package/imaging/tools/custom/rectangleRoiOverlayTool.js +0 -564
- package/imaging/tools/custom/seedTool.js +0 -342
- package/imaging/tools/custom/setLabelMap3D.ts +0 -242
- package/imaging/tools/custom/thresholdsBrushTool.js +0 -161
- package/imaging/tools/default.ts +0 -594
- package/imaging/tools/interaction.ts +0 -266
- package/imaging/tools/io.ts +0 -229
- package/imaging/tools/main.ts +0 -424
- package/imaging/tools/segmentation.ts +0 -532
- package/imaging/tools/segmentations.md +0 -38
- package/imaging/tools/state.ts +0 -74
- package/imaging/tools/strategies/eraseFreehand.js +0 -76
- package/imaging/tools/strategies/fillFreehand.js +0 -79
- package/imaging/tools/strategies/index.js +0 -2
- package/imaging/tools/types.d.ts +0 -243
- package/imaging/types.d.ts +0 -200
- package/imaging/waveforms/ecg.ts +0 -191
- package/index.ts +0 -431
- package/jsdoc.json +0 -52
- package/rollup.config.js +0 -51
- package/template/.gitkeep +0 -0
- package/tsconfig.json +0 -102
|
@@ -1,1884 +0,0 @@
|
|
|
1
|
-
/** @module imaging/tools/custom/contourTool
|
|
2
|
-
* @desc This file provides functionalities for
|
|
3
|
-
* rendering segmentation contours with a
|
|
4
|
-
* custom cornestone tool
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// external libraries
|
|
8
|
-
import cornerstone from "cornerstone-core";
|
|
9
|
-
import csTools from "cornerstone-tools";
|
|
10
|
-
import { each, map } from "lodash";
|
|
11
|
-
|
|
12
|
-
// internal libraries
|
|
13
|
-
import { addToolStateSingleSlice } from "../../imageTools";
|
|
14
|
-
|
|
15
|
-
// cornerstone tools imports
|
|
16
|
-
const external = csTools.external;
|
|
17
|
-
const EVENTS = csTools.EVENTS;
|
|
18
|
-
const BaseAnnotationTool = csTools.importInternal("base/BaseAnnotationTool");
|
|
19
|
-
const getToolState = csTools.getToolState;
|
|
20
|
-
const addToolState = csTools.addToolState;
|
|
21
|
-
const removeToolState = csTools.removeToolState;
|
|
22
|
-
const toolStyle = csTools.toolStyle;
|
|
23
|
-
const toolColors = csTools.toolColors;
|
|
24
|
-
const triggerEvent = csTools.importInternal("util/triggerEvent");
|
|
25
|
-
const pointInsideBoundingBox = csTools.importInternal(
|
|
26
|
-
"util/pointInsideBoundingBox"
|
|
27
|
-
);
|
|
28
|
-
const calculateSUV = csTools.importInternal("util/calculateSUV");
|
|
29
|
-
const numbersWithCommas = csTools.importInternal("util/numbersWithCommas");
|
|
30
|
-
const getNewContext = csTools.importInternal("drawing/getNewContext");
|
|
31
|
-
const draw = csTools.importInternal("drawing/draw");
|
|
32
|
-
const drawJoinedLines = csTools.importInternal("drawing/drawJoinedLines");
|
|
33
|
-
const drawHandles = csTools.importInternal("drawing/drawHandles");
|
|
34
|
-
const drawLinkedTextBox = csTools.importInternal("drawing/drawLinkedTextBox");
|
|
35
|
-
const clipToBox = csTools.importInternal("util/clip");
|
|
36
|
-
const freehandRoiCursor = csTools.importInternal("cursors/freehandRoiCursor");
|
|
37
|
-
const getLogger = csTools.importInternal("util/getLogger");
|
|
38
|
-
const throttle = csTools.importInternal("util/throttle");
|
|
39
|
-
const logger = getLogger("tools:annotation:FreehandRoiTool");
|
|
40
|
-
const freehandUtils = csTools.importInternal("util/freehandUtils");
|
|
41
|
-
|
|
42
|
-
// TODO check how to import these
|
|
43
|
-
// const toolCursor = csTools.importInternal("store/setToolCursor");
|
|
44
|
-
// const hideToolCursor = toolCursor.hideToolCursor;
|
|
45
|
-
// const setToolCursor = toolCursor.setToolCursor;
|
|
46
|
-
// const findAndMoveHelpers = csTools.importInternal("util/findAndMoveHelpers");
|
|
47
|
-
// const moveHandleNearImagePoint = findAndMoveHelpers.moveHandleNearImagePoint;
|
|
48
|
-
|
|
49
|
-
const {
|
|
50
|
-
insertOrDelete,
|
|
51
|
-
freehandArea,
|
|
52
|
-
calculateFreehandStatistics,
|
|
53
|
-
freehandIntersect,
|
|
54
|
-
FreehandHandleData
|
|
55
|
-
} = freehandUtils;
|
|
56
|
-
|
|
57
|
-
const state = {
|
|
58
|
-
// Global
|
|
59
|
-
globalTools: {},
|
|
60
|
-
globalToolChangeHistory: [],
|
|
61
|
-
// Tracking
|
|
62
|
-
enabledElements: [],
|
|
63
|
-
tools: [],
|
|
64
|
-
isToolLocked: false,
|
|
65
|
-
activeMultiPartTool: null,
|
|
66
|
-
mousePositionImage: {},
|
|
67
|
-
// Settings
|
|
68
|
-
clickProximity: 6,
|
|
69
|
-
touchProximity: 10,
|
|
70
|
-
handleRadius: 6,
|
|
71
|
-
deleteIfHandleOutsideImage: true,
|
|
72
|
-
preventHandleOutsideImage: false,
|
|
73
|
-
// Cursor
|
|
74
|
-
svgCursorUrl: null
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* @public
|
|
79
|
-
* @class ContoursTool
|
|
80
|
-
* @memberof Tools.Annotation
|
|
81
|
-
* @classdesc Tool for drawing a set of contours
|
|
82
|
-
* @extends Tools.Base.BaseAnnotationTool
|
|
83
|
-
*/
|
|
84
|
-
export class ContoursTool extends BaseAnnotationTool {
|
|
85
|
-
constructor(props = {}) {
|
|
86
|
-
const defaultProps = {
|
|
87
|
-
name: "ContoursTool",
|
|
88
|
-
supportedInteractionTypes: ["Mouse", "Touch"],
|
|
89
|
-
configuration: defaultFreehandConfiguration(),
|
|
90
|
-
svgCursor: freehandRoiCursor
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
super(props, defaultProps);
|
|
94
|
-
|
|
95
|
-
this.initializeContours(props.contoursParsedData, props.segmentationName);
|
|
96
|
-
|
|
97
|
-
this.isMultiPartTool = true;
|
|
98
|
-
|
|
99
|
-
this._drawing = false;
|
|
100
|
-
this._dragging = false;
|
|
101
|
-
this._modifying = false;
|
|
102
|
-
|
|
103
|
-
// Create bound callback functions for private event loops
|
|
104
|
-
this._drawingMouseDownCallback = this._drawingMouseDownCallback.bind(this);
|
|
105
|
-
this._drawingMouseMoveCallback = this._drawingMouseMoveCallback.bind(this);
|
|
106
|
-
this._drawingMouseDragCallback = this._drawingMouseDragCallback.bind(this);
|
|
107
|
-
this._drawingMouseUpCallback = this._drawingMouseUpCallback.bind(this);
|
|
108
|
-
this._drawingMouseDoubleClickCallback =
|
|
109
|
-
this._drawingMouseDoubleClickCallback.bind(this);
|
|
110
|
-
this._editMouseUpCallback = this._editMouseUpCallback.bind(this);
|
|
111
|
-
this._editMouseDragCallback = this._editMouseDragCallback.bind(this);
|
|
112
|
-
|
|
113
|
-
this._drawingTouchStartCallback =
|
|
114
|
-
this._drawingTouchStartCallback.bind(this);
|
|
115
|
-
this._drawingTouchDragCallback = this._drawingTouchDragCallback.bind(this);
|
|
116
|
-
this._drawingDoubleTapClickCallback =
|
|
117
|
-
this._drawingDoubleTapClickCallback.bind(this);
|
|
118
|
-
this._editTouchDragCallback = this._editTouchDragCallback.bind(this);
|
|
119
|
-
|
|
120
|
-
this.throttledUpdateCachedStats = throttle(this.updateCachedStats, 110);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
initializeContours(contourData, segmentationName) {
|
|
124
|
-
var elements = cornerstone.getEnabledElements();
|
|
125
|
-
each(elements, el => {
|
|
126
|
-
var slices = contourData[el.element.id][segmentationName];
|
|
127
|
-
|
|
128
|
-
for (var slice in slices) {
|
|
129
|
-
var linesPerSlice =
|
|
130
|
-
contourData[el.element.id][segmentationName][slice].lines;
|
|
131
|
-
var lines = map(linesPerSlice, line => {
|
|
132
|
-
let dataToInject = {
|
|
133
|
-
visible: true,
|
|
134
|
-
active: false,
|
|
135
|
-
invalidated: false,
|
|
136
|
-
color: "#FF0000",
|
|
137
|
-
handles: {
|
|
138
|
-
points: line
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
return dataToInject;
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
addToolStateSingleSlice(el.element, "ContoursTool", lines, slice);
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
createNewMeasurement(eventData) {
|
|
150
|
-
const goodEventData =
|
|
151
|
-
eventData && eventData.currentPoints && eventData.currentPoints.image;
|
|
152
|
-
|
|
153
|
-
if (!goodEventData) {
|
|
154
|
-
logger.error(
|
|
155
|
-
`required eventData not supplied to tool ${this.name}'s createNewMeasurement`
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const measurementData = {
|
|
162
|
-
visible: true,
|
|
163
|
-
active: true,
|
|
164
|
-
invalidated: true,
|
|
165
|
-
color: undefined,
|
|
166
|
-
handles: {
|
|
167
|
-
points: []
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
measurementData.handles.textBox = {
|
|
172
|
-
active: false,
|
|
173
|
-
hasMoved: false,
|
|
174
|
-
movesIndependently: false,
|
|
175
|
-
drawnIndependently: true,
|
|
176
|
-
allowedOutsideImage: true,
|
|
177
|
-
hasBoundingBox: true
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
return measurementData;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
* @param {*} element element
|
|
187
|
-
* @param {*} data data
|
|
188
|
-
* @param {*} coords coords
|
|
189
|
-
* @returns {Boolean}
|
|
190
|
-
*/
|
|
191
|
-
pointNearTool(element, data, coords) {
|
|
192
|
-
const validParameters = data && data.handles && data.handles.points;
|
|
193
|
-
|
|
194
|
-
if (!validParameters) {
|
|
195
|
-
throw new Error(
|
|
196
|
-
`invalid parameters supplied to tool ${this.name}'s pointNearTool`
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (!validParameters || data.visible === false) {
|
|
201
|
-
return false;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const isPointNearTool = this._pointNearHandle(element, data, coords);
|
|
205
|
-
|
|
206
|
-
if (isPointNearTool !== undefined) {
|
|
207
|
-
return true;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return false;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* @param {*} element
|
|
215
|
-
* @param {*} data
|
|
216
|
-
* @param {*} coords
|
|
217
|
-
* @returns {number} the distance in px from the provided coordinates to the
|
|
218
|
-
* closest rendered portion of the annotation. -1 if the distance cannot be
|
|
219
|
-
* calculated.
|
|
220
|
-
*/
|
|
221
|
-
distanceFromPoint(element, data, coords) {
|
|
222
|
-
let distance = Infinity;
|
|
223
|
-
|
|
224
|
-
for (let i = 0; i < data.handles.points.length; i++) {
|
|
225
|
-
const distanceI = external.cornerstoneMath.point.distance(
|
|
226
|
-
data.handles.points[i],
|
|
227
|
-
coords
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
distance = Math.min(distance, distanceI);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// If an error caused distance not to be calculated, return -1.
|
|
234
|
-
if (distance === Infinity) {
|
|
235
|
-
return -1;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
return distance;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* @param {*} element
|
|
243
|
-
* @param {*} data
|
|
244
|
-
* @param {*} coords
|
|
245
|
-
* @returns {number} the distance in canvas units from the provided coordinates to the
|
|
246
|
-
* closest rendered portion of the annotation. -1 if the distance cannot be
|
|
247
|
-
* calculated.
|
|
248
|
-
*/
|
|
249
|
-
distanceFromPointCanvas(element, data, coords) {
|
|
250
|
-
let distance = Infinity;
|
|
251
|
-
|
|
252
|
-
if (!data) {
|
|
253
|
-
return -1;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const canvasCoords = external.cornerstone.pixelToCanvas(element, coords);
|
|
257
|
-
|
|
258
|
-
const points = data.handles.points;
|
|
259
|
-
|
|
260
|
-
for (let i = 0; i < points.length; i++) {
|
|
261
|
-
const handleCanvas = external.cornerstone.pixelToCanvas(
|
|
262
|
-
element,
|
|
263
|
-
points[i]
|
|
264
|
-
);
|
|
265
|
-
|
|
266
|
-
const distanceI = external.cornerstoneMath.point.distance(
|
|
267
|
-
handleCanvas,
|
|
268
|
-
canvasCoords
|
|
269
|
-
);
|
|
270
|
-
|
|
271
|
-
distance = Math.min(distance, distanceI);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// If an error caused distance not to be calculated, return -1.
|
|
275
|
-
if (distance === Infinity) {
|
|
276
|
-
return -1;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
return distance;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
* @param {Object} image image
|
|
287
|
-
* @param {Object} element element
|
|
288
|
-
* @param {Object} data data
|
|
289
|
-
*
|
|
290
|
-
* @returns {void} void
|
|
291
|
-
*/
|
|
292
|
-
updateCachedStats(image, element, data) {
|
|
293
|
-
// Define variables for the area and mean/standard deviation
|
|
294
|
-
let meanStdDev, meanStdDevSUV;
|
|
295
|
-
|
|
296
|
-
const seriesModule = external.cornerstone.metaData.get(
|
|
297
|
-
"generalSeriesModule",
|
|
298
|
-
image.imageId
|
|
299
|
-
);
|
|
300
|
-
const modality = seriesModule ? seriesModule.modality : null;
|
|
301
|
-
|
|
302
|
-
const points = data.handles.points;
|
|
303
|
-
// If the data has been invalidated, and the tool is not currently active,
|
|
304
|
-
// We need to calculate it again.
|
|
305
|
-
|
|
306
|
-
// Retrieve the bounds of the ROI in image coordinates
|
|
307
|
-
const bounds = {
|
|
308
|
-
left: points[0].x,
|
|
309
|
-
right: points[0].x,
|
|
310
|
-
bottom: points[0].y,
|
|
311
|
-
top: points[0].x
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
for (let i = 0; i < points.length; i++) {
|
|
315
|
-
bounds.left = Math.min(bounds.left, points[i].x);
|
|
316
|
-
bounds.right = Math.max(bounds.right, points[i].x);
|
|
317
|
-
bounds.bottom = Math.min(bounds.bottom, points[i].y);
|
|
318
|
-
bounds.top = Math.max(bounds.top, points[i].y);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
const polyBoundingBox = {
|
|
322
|
-
left: bounds.left,
|
|
323
|
-
top: bounds.bottom,
|
|
324
|
-
width: Math.abs(bounds.right - bounds.left),
|
|
325
|
-
height: Math.abs(bounds.top - bounds.bottom)
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
// Store the bounding box information for the text box
|
|
329
|
-
data.polyBoundingBox = polyBoundingBox;
|
|
330
|
-
|
|
331
|
-
// First, make sure this is not a color image, since no mean / standard
|
|
332
|
-
// Deviation will be calculated for color images.
|
|
333
|
-
if (!image.color) {
|
|
334
|
-
// Retrieve the array of pixels that the ROI bounds cover
|
|
335
|
-
const pixels = external.cornerstone.getPixels(
|
|
336
|
-
element,
|
|
337
|
-
polyBoundingBox.left,
|
|
338
|
-
polyBoundingBox.top,
|
|
339
|
-
polyBoundingBox.width,
|
|
340
|
-
polyBoundingBox.height
|
|
341
|
-
);
|
|
342
|
-
|
|
343
|
-
// Calculate the mean & standard deviation from the pixels and the object shape
|
|
344
|
-
meanStdDev = calculateFreehandStatistics.call(
|
|
345
|
-
this,
|
|
346
|
-
pixels,
|
|
347
|
-
polyBoundingBox,
|
|
348
|
-
data.handles.points
|
|
349
|
-
);
|
|
350
|
-
|
|
351
|
-
if (modality === "PT") {
|
|
352
|
-
// If the image is from a PET scan, use the DICOM tags to
|
|
353
|
-
// Calculate the SUV from the mean and standard deviation.
|
|
354
|
-
|
|
355
|
-
// Note that because we are using modality pixel values from getPixels, and
|
|
356
|
-
// The calculateSUV routine also rescales to modality pixel values, we are first
|
|
357
|
-
// Returning the values to storedPixel values before calcuating SUV with them.
|
|
358
|
-
// TODO: Clean this up? Should we add an option to not scale in calculateSUV?
|
|
359
|
-
meanStdDevSUV = {
|
|
360
|
-
mean: calculateSUV(
|
|
361
|
-
image,
|
|
362
|
-
(meanStdDev.mean - image.intercept) / image.slope
|
|
363
|
-
),
|
|
364
|
-
stdDev: calculateSUV(
|
|
365
|
-
image,
|
|
366
|
-
(meanStdDev.stdDev - image.intercept) / image.slope
|
|
367
|
-
)
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
// If the mean and standard deviation values are sane, store them for later retrieval
|
|
372
|
-
if (meanStdDev && !isNaN(meanStdDev.mean)) {
|
|
373
|
-
data.meanStdDev = meanStdDev;
|
|
374
|
-
data.meanStdDevSUV = meanStdDevSUV;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// Retrieve the pixel spacing values, and if they are not
|
|
379
|
-
// Real non-zero values, set them to 1
|
|
380
|
-
const columnPixelSpacing = image.columnPixelSpacing || 1;
|
|
381
|
-
const rowPixelSpacing = image.rowPixelSpacing || 1;
|
|
382
|
-
const scaling = columnPixelSpacing * rowPixelSpacing;
|
|
383
|
-
|
|
384
|
-
const area = freehandArea(data.handles.points, scaling);
|
|
385
|
-
|
|
386
|
-
// If the area value is sane, store it for later retrieval
|
|
387
|
-
if (!isNaN(area)) {
|
|
388
|
-
data.area = area;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// Set the invalidated flag to false so that this data won't automatically be recalculated
|
|
392
|
-
data.invalidated = false;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
*
|
|
397
|
-
*
|
|
398
|
-
* @param {*} evt
|
|
399
|
-
* @returns {undefined}
|
|
400
|
-
*/
|
|
401
|
-
renderToolData(evt) {
|
|
402
|
-
const eventData = evt.detail;
|
|
403
|
-
|
|
404
|
-
// If we have no toolState for this element, return immediately as there is nothing to do
|
|
405
|
-
const toolState = getToolState(evt.currentTarget, this.name);
|
|
406
|
-
|
|
407
|
-
if (!toolState) {
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
const { image, element } = eventData;
|
|
412
|
-
const config = this.configuration;
|
|
413
|
-
const seriesModule = external.cornerstone.metaData.get(
|
|
414
|
-
"generalSeriesModule",
|
|
415
|
-
image.imageId
|
|
416
|
-
);
|
|
417
|
-
const modality = seriesModule ? seriesModule.modality : null;
|
|
418
|
-
|
|
419
|
-
// We have tool data for this element - iterate over each one and draw it
|
|
420
|
-
const context = getNewContext(eventData.canvasContext.canvas);
|
|
421
|
-
const lineWidth = toolStyle.getToolWidth();
|
|
422
|
-
|
|
423
|
-
for (let i = 0; i < toolState.data.length; i++) {
|
|
424
|
-
const data = toolState.data[i];
|
|
425
|
-
|
|
426
|
-
if (data.visible === false) {
|
|
427
|
-
continue;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
draw(context, context => {
|
|
431
|
-
let color = toolColors.getColorIfActive(data);
|
|
432
|
-
let fillColor;
|
|
433
|
-
|
|
434
|
-
if (data.active) {
|
|
435
|
-
if (data.handles.invalidHandlePlacement) {
|
|
436
|
-
color = config.invalidColor;
|
|
437
|
-
fillColor = config.invalidColor;
|
|
438
|
-
} else {
|
|
439
|
-
color = toolColors.getColorIfActive(data);
|
|
440
|
-
fillColor = toolColors.getFillColor();
|
|
441
|
-
}
|
|
442
|
-
} else {
|
|
443
|
-
fillColor = toolColors.getToolColor();
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
if (data.handles.points.length) {
|
|
447
|
-
for (let j = 0; j < data.handles.points.length; j++) {
|
|
448
|
-
const lines = [...data.handles.points[j].lines];
|
|
449
|
-
const points = data.handles.points;
|
|
450
|
-
|
|
451
|
-
if (j === points.length - 1 && !data.polyBoundingBox) {
|
|
452
|
-
// If it's still being actively drawn, keep the last line to
|
|
453
|
-
// The mouse location
|
|
454
|
-
lines.push(config.mouseLocation.handles.start);
|
|
455
|
-
}
|
|
456
|
-
drawJoinedLines(context, element, data.handles.points[j], lines, {
|
|
457
|
-
color
|
|
458
|
-
});
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// Draw handles
|
|
463
|
-
|
|
464
|
-
const options = {
|
|
465
|
-
color,
|
|
466
|
-
fill: fillColor
|
|
467
|
-
};
|
|
468
|
-
|
|
469
|
-
if (config.alwaysShowHandles || (data.active && data.polyBoundingBox)) {
|
|
470
|
-
// Render all handles
|
|
471
|
-
options.handleRadius = config.activeHandleRadius;
|
|
472
|
-
drawHandles(context, eventData, data.handles.points, options);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
if (data.canComplete) {
|
|
476
|
-
// Draw large handle at the origin if can complete drawing
|
|
477
|
-
options.handleRadius = config.completeHandleRadius;
|
|
478
|
-
const handle = data.handles.points[0];
|
|
479
|
-
drawHandles(context, eventData, [handle], options);
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
if (data.active && !data.polyBoundingBox) {
|
|
483
|
-
// Draw handle at origin and at mouse if actively drawing
|
|
484
|
-
options.handleRadius = config.activeHandleRadius;
|
|
485
|
-
drawHandles(
|
|
486
|
-
context,
|
|
487
|
-
eventData,
|
|
488
|
-
config.mouseLocation.handles,
|
|
489
|
-
options
|
|
490
|
-
);
|
|
491
|
-
|
|
492
|
-
const firstHandle = data.handles.points[0];
|
|
493
|
-
drawHandles(context, eventData, [firstHandle], options);
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
// Update textbox stats
|
|
497
|
-
if (data.invalidated === true && !data.active) {
|
|
498
|
-
if (data.meanStdDev && data.meanStdDevSUV && data.area) {
|
|
499
|
-
this.throttledUpdateCachedStats(image, element, data);
|
|
500
|
-
} else {
|
|
501
|
-
this.updateCachedStats(image, element, data);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
// Only render text if polygon ROI has been completed and freehand 'shiftKey' mode was not used:
|
|
506
|
-
if (data.polyBoundingBox && !data.handles.textBox.freehand) {
|
|
507
|
-
// If the textbox has not been moved by the user, it should be displayed on the right-most
|
|
508
|
-
// Side of the tool.
|
|
509
|
-
if (!data.handles.textBox.hasMoved) {
|
|
510
|
-
// Find the rightmost side of the polyBoundingBox at its vertical center, and place the textbox here
|
|
511
|
-
// Note that this calculates it in image coordinates
|
|
512
|
-
data.handles.textBox.x =
|
|
513
|
-
data.polyBoundingBox.left + data.polyBoundingBox.width;
|
|
514
|
-
data.handles.textBox.y =
|
|
515
|
-
data.polyBoundingBox.top + data.polyBoundingBox.height / 2;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
const text = textBoxText.call(this, data);
|
|
519
|
-
|
|
520
|
-
drawLinkedTextBox(
|
|
521
|
-
context,
|
|
522
|
-
element,
|
|
523
|
-
data.handles.textBox,
|
|
524
|
-
text,
|
|
525
|
-
data.handles.points,
|
|
526
|
-
textBoxAnchorPoints,
|
|
527
|
-
color,
|
|
528
|
-
lineWidth,
|
|
529
|
-
0,
|
|
530
|
-
true
|
|
531
|
-
);
|
|
532
|
-
}
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
function textBoxText(data) {
|
|
537
|
-
const { meanStdDev, meanStdDevSUV, area } = data;
|
|
538
|
-
// Define an array to store the rows of text for the textbox
|
|
539
|
-
const textLines = [];
|
|
540
|
-
|
|
541
|
-
// If the mean and standard deviation values are present, display them
|
|
542
|
-
if (meanStdDev && meanStdDev.mean !== undefined) {
|
|
543
|
-
// If the modality is CT, add HU to denote Hounsfield Units
|
|
544
|
-
let moSuffix = "";
|
|
545
|
-
|
|
546
|
-
if (modality === "CT") {
|
|
547
|
-
moSuffix = "HU";
|
|
548
|
-
}
|
|
549
|
-
data.unit = moSuffix;
|
|
550
|
-
|
|
551
|
-
// Create a line of text to display the mean and any units that were specified (i.e. HU)
|
|
552
|
-
let meanText = `Mean: ${numbersWithCommas(
|
|
553
|
-
meanStdDev.mean.toFixed(2)
|
|
554
|
-
)} ${moSuffix}`;
|
|
555
|
-
// Create a line of text to display the standard deviation and any units that were specified (i.e. HU)
|
|
556
|
-
let stdDevText = `StdDev: ${numbersWithCommas(
|
|
557
|
-
meanStdDev.stdDev.toFixed(2)
|
|
558
|
-
)} ${moSuffix}`;
|
|
559
|
-
|
|
560
|
-
// If this image has SUV values to display, concatenate them to the text line
|
|
561
|
-
if (meanStdDevSUV && meanStdDevSUV.mean !== undefined) {
|
|
562
|
-
const SUVtext = " SUV: ";
|
|
563
|
-
|
|
564
|
-
meanText +=
|
|
565
|
-
SUVtext + numbersWithCommas(meanStdDevSUV.mean.toFixed(2));
|
|
566
|
-
stdDevText +=
|
|
567
|
-
SUVtext + numbersWithCommas(meanStdDevSUV.stdDev.toFixed(2));
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
// Add these text lines to the array to be displayed in the textbox
|
|
571
|
-
textLines.push(meanText);
|
|
572
|
-
textLines.push(stdDevText);
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
// If the area is a sane value, display it
|
|
576
|
-
if (area) {
|
|
577
|
-
// Determine the area suffix based on the pixel spacing in the image.
|
|
578
|
-
// If pixel spacing is present, use millimeters. Otherwise, use pixels.
|
|
579
|
-
// This uses Char code 178 for a superscript 2
|
|
580
|
-
let suffix = ` mm${String.fromCharCode(178)}`;
|
|
581
|
-
|
|
582
|
-
if (!image.rowPixelSpacing || !image.columnPixelSpacing) {
|
|
583
|
-
suffix = ` pixels${String.fromCharCode(178)}`;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
// Create a line of text to display the area and its units
|
|
587
|
-
const areaText = `Area: ${numbersWithCommas(area.toFixed(2))}${suffix}`;
|
|
588
|
-
|
|
589
|
-
// Add this text line to the array to be displayed in the textbox
|
|
590
|
-
textLines.push(areaText);
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
return textLines;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
function textBoxAnchorPoints(handles) {
|
|
597
|
-
return handles;
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
addNewMeasurement(evt) {
|
|
602
|
-
const eventData = evt.detail;
|
|
603
|
-
|
|
604
|
-
this._startDrawing(evt);
|
|
605
|
-
this._addPoint(eventData);
|
|
606
|
-
|
|
607
|
-
preventPropagation(evt);
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
preMouseDownCallback(evt) {
|
|
611
|
-
const eventData = evt.detail;
|
|
612
|
-
const nearby = this._pointNearHandleAllTools(eventData);
|
|
613
|
-
|
|
614
|
-
if (eventData.event.ctrlKey) {
|
|
615
|
-
if (nearby !== undefined && nearby.handleNearby.hasBoundingBox) {
|
|
616
|
-
// Ctrl + clicked textBox, do nothing but still consume event.
|
|
617
|
-
} else {
|
|
618
|
-
insertOrDelete.call(this, evt, nearby);
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
preventPropagation(evt);
|
|
622
|
-
|
|
623
|
-
return true;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
return false;
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
handleSelectedCallback(evt, toolData, handle, interactionType = "mouse") {
|
|
630
|
-
const { element } = evt.detail;
|
|
631
|
-
const toolState = getToolState(element, this.name);
|
|
632
|
-
console.info(interactionType);
|
|
633
|
-
// if (handle.hasBoundingBox) {
|
|
634
|
-
// // Use default move handler.
|
|
635
|
-
// moveHandleNearImagePoint(evt, this, toolData, handle, interactionType);
|
|
636
|
-
|
|
637
|
-
// return;
|
|
638
|
-
// }
|
|
639
|
-
|
|
640
|
-
const config = this.configuration;
|
|
641
|
-
|
|
642
|
-
config.dragOrigin = {
|
|
643
|
-
x: handle.x,
|
|
644
|
-
y: handle.y
|
|
645
|
-
};
|
|
646
|
-
|
|
647
|
-
// Iterating over handles of all toolData instances to find the indices of the selected handle
|
|
648
|
-
for (let toolIndex = 0; toolIndex < toolState.data.length; toolIndex++) {
|
|
649
|
-
const points = toolState.data[toolIndex].handles.points;
|
|
650
|
-
|
|
651
|
-
for (let p = 0; p < points.length; p++) {
|
|
652
|
-
if (points[p] === handle) {
|
|
653
|
-
config.currentHandle = p;
|
|
654
|
-
config.currentTool = toolIndex;
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
this._modifying = true;
|
|
660
|
-
|
|
661
|
-
this._activateModify(element);
|
|
662
|
-
|
|
663
|
-
// Interupt eventDispatchers
|
|
664
|
-
preventPropagation(evt);
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
/**
|
|
668
|
-
* Event handler for MOUSE_MOVE during drawing event loop.
|
|
669
|
-
*
|
|
670
|
-
* @event
|
|
671
|
-
* @param {Object} evt - The event.
|
|
672
|
-
* @returns {undefined}
|
|
673
|
-
*/
|
|
674
|
-
_drawingMouseMoveCallback(evt) {
|
|
675
|
-
const eventData = evt.detail;
|
|
676
|
-
const { currentPoints, element } = eventData;
|
|
677
|
-
const toolState = getToolState(element, this.name);
|
|
678
|
-
|
|
679
|
-
const config = this.configuration;
|
|
680
|
-
const currentTool = config.currentTool;
|
|
681
|
-
|
|
682
|
-
const data = toolState.data[currentTool];
|
|
683
|
-
const coords = currentPoints.canvas;
|
|
684
|
-
|
|
685
|
-
// Set the mouseLocation handle
|
|
686
|
-
this._getMouseLocation(eventData);
|
|
687
|
-
this._checkInvalidHandleLocation(data, eventData);
|
|
688
|
-
|
|
689
|
-
// Mouse move -> Polygon Mode
|
|
690
|
-
const handleNearby = this._pointNearHandle(element, data, coords);
|
|
691
|
-
const points = data.handles.points;
|
|
692
|
-
// If there is a handle nearby to snap to
|
|
693
|
-
// (and it's not the actual mouse handle)
|
|
694
|
-
|
|
695
|
-
if (
|
|
696
|
-
handleNearby !== undefined &&
|
|
697
|
-
!handleNearby.hasBoundingBox &&
|
|
698
|
-
handleNearby < points.length - 1
|
|
699
|
-
) {
|
|
700
|
-
config.mouseLocation.handles.start.x = points[handleNearby].x;
|
|
701
|
-
config.mouseLocation.handles.start.y = points[handleNearby].y;
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
// Force onImageRendered
|
|
705
|
-
external.cornerstone.updateImage(element);
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
/**
|
|
709
|
-
* Event handler for MOUSE_DRAG during drawing event loop.
|
|
710
|
-
*
|
|
711
|
-
* @event
|
|
712
|
-
* @param {Object} evt - The event.
|
|
713
|
-
* @returns {undefined}
|
|
714
|
-
*/
|
|
715
|
-
_drawingMouseDragCallback(evt) {
|
|
716
|
-
if (!this.options.mouseButtonMask.includes(evt.detail.buttons)) {
|
|
717
|
-
return;
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
this._drawingDrag(evt);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
/**
|
|
724
|
-
* Event handler for TOUCH_DRAG during drawing event loop.
|
|
725
|
-
*
|
|
726
|
-
* @event
|
|
727
|
-
* @param {Object} evt - The event.
|
|
728
|
-
* @returns {undefined}
|
|
729
|
-
*/
|
|
730
|
-
_drawingTouchDragCallback(evt) {
|
|
731
|
-
this._drawingDrag(evt);
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
_drawingDrag(evt) {
|
|
735
|
-
const eventData = evt.detail;
|
|
736
|
-
const { element } = eventData;
|
|
737
|
-
|
|
738
|
-
const toolState = getToolState(element, this.name);
|
|
739
|
-
|
|
740
|
-
const config = this.configuration;
|
|
741
|
-
const currentTool = config.currentTool;
|
|
742
|
-
|
|
743
|
-
const data = toolState.data[currentTool];
|
|
744
|
-
|
|
745
|
-
// Set the mouseLocation handle
|
|
746
|
-
this._getMouseLocation(eventData);
|
|
747
|
-
this._checkInvalidHandleLocation(data, eventData);
|
|
748
|
-
this._addPointPencilMode(eventData, data.handles.points);
|
|
749
|
-
this._dragging = true;
|
|
750
|
-
|
|
751
|
-
// Force onImageRendered
|
|
752
|
-
external.cornerstone.updateImage(element);
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
/**
|
|
756
|
-
* Event handler for MOUSE_UP during drawing event loop.
|
|
757
|
-
*
|
|
758
|
-
* @event
|
|
759
|
-
* @param {Object} evt - The event.
|
|
760
|
-
* @returns {undefined}
|
|
761
|
-
*/
|
|
762
|
-
_drawingMouseUpCallback(evt) {
|
|
763
|
-
const { element } = evt.detail;
|
|
764
|
-
|
|
765
|
-
if (!this._dragging) {
|
|
766
|
-
return;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
this._dragging = false;
|
|
770
|
-
|
|
771
|
-
const config = this.configuration;
|
|
772
|
-
const currentTool = config.currentTool;
|
|
773
|
-
const toolState = getToolState(element, this.name);
|
|
774
|
-
const data = toolState.data[currentTool];
|
|
775
|
-
|
|
776
|
-
if (!freehandIntersect.end(data.handles.points) && data.canComplete) {
|
|
777
|
-
const lastHandlePlaced = config.currentHandle;
|
|
778
|
-
|
|
779
|
-
this._endDrawing(element, lastHandlePlaced);
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
preventPropagation(evt);
|
|
783
|
-
|
|
784
|
-
return;
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
/**
|
|
788
|
-
* Event handler for MOUSE_DOWN during drawing event loop.
|
|
789
|
-
*
|
|
790
|
-
* @event
|
|
791
|
-
* @param {Object} evt - The event.
|
|
792
|
-
* @returns {undefined}
|
|
793
|
-
*/
|
|
794
|
-
_drawingMouseDownCallback(evt) {
|
|
795
|
-
const eventData = evt.detail;
|
|
796
|
-
const { buttons, currentPoints, element } = eventData;
|
|
797
|
-
|
|
798
|
-
if (!this.options.mouseButtonMask.includes(buttons)) {
|
|
799
|
-
return;
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
const coords = currentPoints.canvas;
|
|
803
|
-
|
|
804
|
-
const config = this.configuration;
|
|
805
|
-
const currentTool = config.currentTool;
|
|
806
|
-
const toolState = getToolState(element, this.name);
|
|
807
|
-
const data = toolState.data[currentTool];
|
|
808
|
-
|
|
809
|
-
const handleNearby = this._pointNearHandle(element, data, coords);
|
|
810
|
-
|
|
811
|
-
if (!freehandIntersect.end(data.handles.points) && data.canComplete) {
|
|
812
|
-
const lastHandlePlaced = config.currentHandle;
|
|
813
|
-
|
|
814
|
-
this._endDrawing(element, lastHandlePlaced);
|
|
815
|
-
} else if (handleNearby === undefined) {
|
|
816
|
-
this._addPoint(eventData);
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
preventPropagation(evt);
|
|
820
|
-
|
|
821
|
-
return;
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
/**
|
|
825
|
-
* Event handler for TOUCH_START during drawing event loop.
|
|
826
|
-
*
|
|
827
|
-
* @event
|
|
828
|
-
* @param {Object} evt - The event.
|
|
829
|
-
* @returns {undefined}
|
|
830
|
-
*/
|
|
831
|
-
_drawingTouchStartCallback(evt) {
|
|
832
|
-
const eventData = evt.detail;
|
|
833
|
-
const { currentPoints, element } = eventData;
|
|
834
|
-
|
|
835
|
-
const coords = currentPoints.canvas;
|
|
836
|
-
|
|
837
|
-
const config = this.configuration;
|
|
838
|
-
const currentTool = config.currentTool;
|
|
839
|
-
const toolState = getToolState(element, this.name);
|
|
840
|
-
const data = toolState.data[currentTool];
|
|
841
|
-
|
|
842
|
-
const handleNearby = this._pointNearHandle(element, data, coords);
|
|
843
|
-
|
|
844
|
-
if (!freehandIntersect.end(data.handles.points) && data.canComplete) {
|
|
845
|
-
const lastHandlePlaced = config.currentHandle;
|
|
846
|
-
|
|
847
|
-
this._endDrawing(element, lastHandlePlaced);
|
|
848
|
-
} else if (handleNearby === undefined) {
|
|
849
|
-
this._addPoint(eventData);
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
preventPropagation(evt);
|
|
853
|
-
|
|
854
|
-
return;
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
/** Ends the active drawing loop and completes the polygon.
|
|
858
|
-
*
|
|
859
|
-
* @public
|
|
860
|
-
* @param {Object} element - The element on which the roi is being drawn.
|
|
861
|
-
* @returns {null}
|
|
862
|
-
*/
|
|
863
|
-
completeDrawing(element) {
|
|
864
|
-
if (!this._drawing) {
|
|
865
|
-
return;
|
|
866
|
-
}
|
|
867
|
-
const toolState = getToolState(element, this.name);
|
|
868
|
-
const config = this.configuration;
|
|
869
|
-
const data = toolState.data[config.currentTool];
|
|
870
|
-
|
|
871
|
-
if (
|
|
872
|
-
!freehandIntersect.end(data.handles.points) &&
|
|
873
|
-
data.handles.points.length >= 2
|
|
874
|
-
) {
|
|
875
|
-
const lastHandlePlaced = config.currentHandle;
|
|
876
|
-
|
|
877
|
-
data.polyBoundingBox = {};
|
|
878
|
-
this._endDrawing(element, lastHandlePlaced);
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
/**
|
|
883
|
-
* Event handler for MOUSE_DOUBLE_CLICK during drawing event loop.
|
|
884
|
-
*
|
|
885
|
-
* @event
|
|
886
|
-
* @param {Object} evt - The event.
|
|
887
|
-
* @returns {undefined}
|
|
888
|
-
*/
|
|
889
|
-
_drawingMouseDoubleClickCallback(evt) {
|
|
890
|
-
const { element } = evt.detail;
|
|
891
|
-
|
|
892
|
-
this.completeDrawing(element);
|
|
893
|
-
|
|
894
|
-
preventPropagation(evt);
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
/**
|
|
898
|
-
* Event handler for DOUBLE_TAP during drawing event loop.
|
|
899
|
-
*
|
|
900
|
-
* @event
|
|
901
|
-
* @param {Object} evt - The event.
|
|
902
|
-
* @returns {undefined}
|
|
903
|
-
*/
|
|
904
|
-
_drawingDoubleTapClickCallback(evt) {
|
|
905
|
-
const { element } = evt.detail;
|
|
906
|
-
|
|
907
|
-
this.completeDrawing(element);
|
|
908
|
-
|
|
909
|
-
preventPropagation(evt);
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
/**
|
|
913
|
-
* Event handler for MOUSE_DRAG during handle drag event loop.
|
|
914
|
-
*
|
|
915
|
-
* @event
|
|
916
|
-
* @param {Object} evt - The event.
|
|
917
|
-
* @returns {undefined}
|
|
918
|
-
*/
|
|
919
|
-
_editMouseDragCallback(evt) {
|
|
920
|
-
const eventData = evt.detail;
|
|
921
|
-
const { element, buttons } = eventData;
|
|
922
|
-
|
|
923
|
-
if (!this.options.mouseButtonMask.includes(buttons)) {
|
|
924
|
-
return;
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
const toolState = getToolState(element, this.name);
|
|
928
|
-
|
|
929
|
-
const config = this.configuration;
|
|
930
|
-
const data = toolState.data[config.currentTool];
|
|
931
|
-
const currentHandle = config.currentHandle;
|
|
932
|
-
const points = data.handles.points;
|
|
933
|
-
let handleIndex = -1;
|
|
934
|
-
|
|
935
|
-
// Set the mouseLocation handle
|
|
936
|
-
this._getMouseLocation(eventData);
|
|
937
|
-
|
|
938
|
-
data.handles.invalidHandlePlacement = freehandIntersect.modify(
|
|
939
|
-
points,
|
|
940
|
-
currentHandle
|
|
941
|
-
);
|
|
942
|
-
data.active = true;
|
|
943
|
-
data.highlight = true;
|
|
944
|
-
points[currentHandle].x = config.mouseLocation.handles.start.x;
|
|
945
|
-
points[currentHandle].y = config.mouseLocation.handles.start.y;
|
|
946
|
-
|
|
947
|
-
handleIndex = this._getPrevHandleIndex(currentHandle, points);
|
|
948
|
-
|
|
949
|
-
if (currentHandle >= 0) {
|
|
950
|
-
const lastLineIndex = points[handleIndex].lines.length - 1;
|
|
951
|
-
const lastLine = points[handleIndex].lines[lastLineIndex];
|
|
952
|
-
|
|
953
|
-
lastLine.x = config.mouseLocation.handles.start.x;
|
|
954
|
-
lastLine.y = config.mouseLocation.handles.start.y;
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
// Update the image
|
|
958
|
-
external.cornerstone.updateImage(element);
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
/**
|
|
962
|
-
* Event handler for TOUCH_DRAG during handle drag event loop.
|
|
963
|
-
*
|
|
964
|
-
* @event
|
|
965
|
-
* @param {Object} evt - The event.
|
|
966
|
-
* @returns {void}
|
|
967
|
-
*/
|
|
968
|
-
_editTouchDragCallback(evt) {
|
|
969
|
-
const eventData = evt.detail;
|
|
970
|
-
const { element } = eventData;
|
|
971
|
-
|
|
972
|
-
const toolState = getToolState(element, this.name);
|
|
973
|
-
|
|
974
|
-
const config = this.configuration;
|
|
975
|
-
const data = toolState.data[config.currentTool];
|
|
976
|
-
const currentHandle = config.currentHandle;
|
|
977
|
-
const points = data.handles.points;
|
|
978
|
-
let handleIndex = -1;
|
|
979
|
-
|
|
980
|
-
// Set the mouseLocation handle
|
|
981
|
-
this._getMouseLocation(eventData);
|
|
982
|
-
|
|
983
|
-
data.handles.invalidHandlePlacement = freehandIntersect.modify(
|
|
984
|
-
points,
|
|
985
|
-
currentHandle
|
|
986
|
-
);
|
|
987
|
-
data.active = true;
|
|
988
|
-
data.highlight = true;
|
|
989
|
-
points[currentHandle].x = config.mouseLocation.handles.start.x;
|
|
990
|
-
points[currentHandle].y = config.mouseLocation.handles.start.y;
|
|
991
|
-
|
|
992
|
-
handleIndex = this._getPrevHandleIndex(currentHandle, points);
|
|
993
|
-
|
|
994
|
-
if (currentHandle >= 0) {
|
|
995
|
-
const lastLineIndex = points[handleIndex].lines.length - 1;
|
|
996
|
-
const lastLine = points[handleIndex].lines[lastLineIndex];
|
|
997
|
-
|
|
998
|
-
lastLine.x = config.mouseLocation.handles.start.x;
|
|
999
|
-
lastLine.y = config.mouseLocation.handles.start.y;
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
// Update the image
|
|
1003
|
-
external.cornerstone.updateImage(element);
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
/**
|
|
1007
|
-
* Returns the previous handle to the current one.
|
|
1008
|
-
* @param {Number} currentHandle - the current handle index
|
|
1009
|
-
* @param {Array} points - the handles Array of the freehand data
|
|
1010
|
-
* @returns {Number} - The index of the previos handle
|
|
1011
|
-
*/
|
|
1012
|
-
_getPrevHandleIndex(currentHandle, points) {
|
|
1013
|
-
if (currentHandle === 0) {
|
|
1014
|
-
return points.length - 1;
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
return currentHandle - 1;
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
/**
|
|
1021
|
-
* Event handler for MOUSE_UP during handle drag event loop.
|
|
1022
|
-
*
|
|
1023
|
-
* @private
|
|
1024
|
-
* @param {Object} evt - The event.
|
|
1025
|
-
* @returns {undefined}
|
|
1026
|
-
*/
|
|
1027
|
-
_editMouseUpCallback(evt) {
|
|
1028
|
-
const eventData = evt.detail;
|
|
1029
|
-
const { element } = eventData;
|
|
1030
|
-
const toolState = getToolState(element, this.name);
|
|
1031
|
-
|
|
1032
|
-
this._deactivateModify(element);
|
|
1033
|
-
|
|
1034
|
-
this._dropHandle(eventData, toolState);
|
|
1035
|
-
this._endDrawing(element);
|
|
1036
|
-
|
|
1037
|
-
external.cornerstone.updateImage(element);
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
/**
|
|
1041
|
-
* Places a handle of the freehand tool if the new location is valid.
|
|
1042
|
-
* If the new location is invalid the handle snaps back to its previous position.
|
|
1043
|
-
*
|
|
1044
|
-
* @private
|
|
1045
|
-
* @param {Object} eventData - Data object associated with the event.
|
|
1046
|
-
* @param {Object} toolState - The data associated with the freehand tool.
|
|
1047
|
-
* @modifies {toolState}
|
|
1048
|
-
* @returns {undefined}
|
|
1049
|
-
*/
|
|
1050
|
-
_dropHandle(eventData, toolState) {
|
|
1051
|
-
const config = this.configuration;
|
|
1052
|
-
const currentTool = config.currentTool;
|
|
1053
|
-
const handles = toolState.data[currentTool].handles;
|
|
1054
|
-
const points = handles.points;
|
|
1055
|
-
|
|
1056
|
-
// Don't allow the line being modified to intersect other lines
|
|
1057
|
-
if (handles.invalidHandlePlacement) {
|
|
1058
|
-
const currentHandle = config.currentHandle;
|
|
1059
|
-
const currentHandleData = points[currentHandle];
|
|
1060
|
-
let previousHandleData;
|
|
1061
|
-
|
|
1062
|
-
if (currentHandle === 0) {
|
|
1063
|
-
const lastHandleID = points.length - 1;
|
|
1064
|
-
|
|
1065
|
-
previousHandleData = points[lastHandleID];
|
|
1066
|
-
} else {
|
|
1067
|
-
previousHandleData = points[currentHandle - 1];
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
// Snap back to previous position
|
|
1071
|
-
currentHandleData.x = config.dragOrigin.x;
|
|
1072
|
-
currentHandleData.y = config.dragOrigin.y;
|
|
1073
|
-
previousHandleData.lines[0] = currentHandleData;
|
|
1074
|
-
|
|
1075
|
-
handles.invalidHandlePlacement = false;
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
/**
|
|
1080
|
-
* Begining of drawing loop when tool is active and a click event happens far
|
|
1081
|
-
* from existing handles.
|
|
1082
|
-
*
|
|
1083
|
-
* @private
|
|
1084
|
-
* @param {Object} evt - The event.
|
|
1085
|
-
* @returns {undefined}
|
|
1086
|
-
*/
|
|
1087
|
-
_startDrawing(evt) {
|
|
1088
|
-
const eventData = evt.detail;
|
|
1089
|
-
const measurementData = this.createNewMeasurement(eventData);
|
|
1090
|
-
const { element } = eventData;
|
|
1091
|
-
const config = this.configuration;
|
|
1092
|
-
let interactionType;
|
|
1093
|
-
|
|
1094
|
-
if (evt.type === EVENTS.MOUSE_DOWN_ACTIVATE) {
|
|
1095
|
-
interactionType = "Mouse";
|
|
1096
|
-
} else if (evt.type === EVENTS.TOUCH_START_ACTIVE) {
|
|
1097
|
-
interactionType = "Touch";
|
|
1098
|
-
}
|
|
1099
|
-
this._activateDraw(element, interactionType);
|
|
1100
|
-
this._getMouseLocation(eventData);
|
|
1101
|
-
|
|
1102
|
-
addToolState(element, this.name, measurementData);
|
|
1103
|
-
|
|
1104
|
-
const toolState = getToolState(element, this.name);
|
|
1105
|
-
|
|
1106
|
-
config.currentTool = toolState.data.length - 1;
|
|
1107
|
-
|
|
1108
|
-
this._activeDrawingToolReference = toolState.data[config.currentTool];
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
/**
|
|
1112
|
-
* Adds a point on mouse click in polygon mode.
|
|
1113
|
-
*
|
|
1114
|
-
* @private
|
|
1115
|
-
* @param {Object} eventData - data object associated with an event.
|
|
1116
|
-
* @returns {undefined}
|
|
1117
|
-
*/
|
|
1118
|
-
_addPoint(eventData) {
|
|
1119
|
-
const { currentPoints, element } = eventData;
|
|
1120
|
-
const toolState = getToolState(element, this.name);
|
|
1121
|
-
|
|
1122
|
-
// Get the toolState from the last-drawn polygon
|
|
1123
|
-
const config = this.configuration;
|
|
1124
|
-
const data = toolState.data[config.currentTool];
|
|
1125
|
-
|
|
1126
|
-
if (data.handles.invalidHandlePlacement) {
|
|
1127
|
-
return;
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
const newHandleData = new FreehandHandleData(currentPoints.image);
|
|
1131
|
-
|
|
1132
|
-
// If this is not the first handle
|
|
1133
|
-
if (data.handles.points.length) {
|
|
1134
|
-
// Add the line from the current handle to the new handle
|
|
1135
|
-
data.handles.points[config.currentHandle - 1].lines.push(
|
|
1136
|
-
currentPoints.image
|
|
1137
|
-
);
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
// Add the new handle
|
|
1141
|
-
data.handles.points.push(newHandleData);
|
|
1142
|
-
|
|
1143
|
-
// Increment the current handle value
|
|
1144
|
-
config.currentHandle += 1;
|
|
1145
|
-
|
|
1146
|
-
// Force onImageRendered to fire
|
|
1147
|
-
external.cornerstone.updateImage(element);
|
|
1148
|
-
this.fireModifiedEvent(element, data);
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
/**
|
|
1152
|
-
* If in pencilMode, check the mouse position is farther than the minimum
|
|
1153
|
-
* distance between points, then add a point.
|
|
1154
|
-
*
|
|
1155
|
-
* @private
|
|
1156
|
-
* @param {Object} eventData - Data object associated with an event.
|
|
1157
|
-
* @param {Object} points - Data object associated with the tool.
|
|
1158
|
-
* @returns {undefined}
|
|
1159
|
-
*/
|
|
1160
|
-
_addPointPencilMode(eventData, points) {
|
|
1161
|
-
const config = this.configuration;
|
|
1162
|
-
const { element } = eventData;
|
|
1163
|
-
const mousePoint = config.mouseLocation.handles.start;
|
|
1164
|
-
|
|
1165
|
-
const handleFurtherThanMinimumSpacing = handle =>
|
|
1166
|
-
this._isDistanceLargerThanSpacing(element, handle, mousePoint);
|
|
1167
|
-
|
|
1168
|
-
if (points.every(handleFurtherThanMinimumSpacing)) {
|
|
1169
|
-
this._addPoint(eventData);
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
/**
|
|
1174
|
-
* Ends the active drawing loop and completes the polygon.
|
|
1175
|
-
*
|
|
1176
|
-
* @private
|
|
1177
|
-
* @param {Object} element - The element on which the roi is being drawn.
|
|
1178
|
-
* @param {Object} handleNearby - the handle nearest to the mouse cursor.
|
|
1179
|
-
* @returns {undefined}
|
|
1180
|
-
*/
|
|
1181
|
-
_endDrawing(element, handleNearby) {
|
|
1182
|
-
const toolState = getToolState(element, this.name);
|
|
1183
|
-
const config = this.configuration;
|
|
1184
|
-
const data = toolState.data[config.currentTool];
|
|
1185
|
-
|
|
1186
|
-
data.active = false;
|
|
1187
|
-
data.highlight = false;
|
|
1188
|
-
data.handles.invalidHandlePlacement = false;
|
|
1189
|
-
|
|
1190
|
-
// Connect the end handle to the origin handle
|
|
1191
|
-
if (handleNearby !== undefined) {
|
|
1192
|
-
const points = data.handles.points;
|
|
1193
|
-
|
|
1194
|
-
points[config.currentHandle - 1].lines.push(points[0]);
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
if (this._modifying) {
|
|
1198
|
-
this._modifying = false;
|
|
1199
|
-
data.invalidated = true;
|
|
1200
|
-
}
|
|
1201
|
-
|
|
1202
|
-
// Reset the current handle
|
|
1203
|
-
config.currentHandle = 0;
|
|
1204
|
-
config.currentTool = -1;
|
|
1205
|
-
data.canComplete = false;
|
|
1206
|
-
|
|
1207
|
-
if (this._drawing) {
|
|
1208
|
-
this._deactivateDraw(element);
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
external.cornerstone.updateImage(element);
|
|
1212
|
-
|
|
1213
|
-
this.fireModifiedEvent(element, data);
|
|
1214
|
-
this.fireCompletedEvent(element, data);
|
|
1215
|
-
}
|
|
1216
|
-
|
|
1217
|
-
/**
|
|
1218
|
-
* Returns a handle of a particular tool if it is close to the mouse cursor
|
|
1219
|
-
*
|
|
1220
|
-
* @private
|
|
1221
|
-
* @param {Object} element - The element on which the roi is being drawn.
|
|
1222
|
-
* @param {Object} data Data object associated with the tool.
|
|
1223
|
-
* @param {*} coords
|
|
1224
|
-
* @returns {Number|Object|Boolean}
|
|
1225
|
-
*/
|
|
1226
|
-
_pointNearHandle(element, data, coords) {
|
|
1227
|
-
if (data.handles === undefined || data.handles.points === undefined) {
|
|
1228
|
-
return;
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
|
-
if (data.visible === false) {
|
|
1232
|
-
return;
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
for (let i = 0; i < data.handles.points.length; i++) {
|
|
1236
|
-
const handleCanvas = external.cornerstone.pixelToCanvas(
|
|
1237
|
-
element,
|
|
1238
|
-
data.handles.points[i]
|
|
1239
|
-
);
|
|
1240
|
-
|
|
1241
|
-
if (external.cornerstoneMath.point.distance(handleCanvas, coords) < 6) {
|
|
1242
|
-
return i;
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
// Check to see if mouse in bounding box of textbox
|
|
1247
|
-
if (data.handles.textBox) {
|
|
1248
|
-
if (pointInsideBoundingBox(data.handles.textBox, coords)) {
|
|
1249
|
-
return data.handles.textBox;
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
|
-
/**
|
|
1255
|
-
* Returns a handle if it is close to the mouse cursor (all tools)
|
|
1256
|
-
*
|
|
1257
|
-
* @private
|
|
1258
|
-
* @param {Object} eventData - data object associated with an event.
|
|
1259
|
-
* @returns {Object}
|
|
1260
|
-
*/
|
|
1261
|
-
_pointNearHandleAllTools(eventData) {
|
|
1262
|
-
const { currentPoints, element } = eventData;
|
|
1263
|
-
const coords = currentPoints.canvas;
|
|
1264
|
-
const toolState = getToolState(element, this.name);
|
|
1265
|
-
|
|
1266
|
-
if (!toolState) {
|
|
1267
|
-
return;
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
let handleNearby;
|
|
1271
|
-
|
|
1272
|
-
for (let toolIndex = 0; toolIndex < toolState.data.length; toolIndex++) {
|
|
1273
|
-
handleNearby = this._pointNearHandle(
|
|
1274
|
-
element,
|
|
1275
|
-
toolState.data[toolIndex],
|
|
1276
|
-
coords
|
|
1277
|
-
);
|
|
1278
|
-
if (handleNearby !== undefined) {
|
|
1279
|
-
return {
|
|
1280
|
-
handleNearby,
|
|
1281
|
-
toolIndex
|
|
1282
|
-
};
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
/**
|
|
1288
|
-
* Gets the current mouse location and stores it in the configuration object.
|
|
1289
|
-
*
|
|
1290
|
-
* @private
|
|
1291
|
-
* @param {Object} eventData The data assoicated with the event.
|
|
1292
|
-
* @returns {undefined}
|
|
1293
|
-
*/
|
|
1294
|
-
_getMouseLocation(eventData) {
|
|
1295
|
-
const { currentPoints, image } = eventData;
|
|
1296
|
-
// Set the mouseLocation handle
|
|
1297
|
-
const config = this.configuration;
|
|
1298
|
-
|
|
1299
|
-
config.mouseLocation.handles.start.x = currentPoints.image.x;
|
|
1300
|
-
config.mouseLocation.handles.start.y = currentPoints.image.y;
|
|
1301
|
-
clipToBox(config.mouseLocation.handles.start, image);
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
|
-
/**
|
|
1305
|
-
* Returns true if the proposed location of a new handle is invalid.
|
|
1306
|
-
*
|
|
1307
|
-
* @private
|
|
1308
|
-
* @param {Object} data Data object associated with the tool.
|
|
1309
|
-
* @param {Object} eventData The data assoicated with the event.
|
|
1310
|
-
* @returns {Boolean}
|
|
1311
|
-
*/
|
|
1312
|
-
_checkInvalidHandleLocation(data, eventData) {
|
|
1313
|
-
if (data.handles.points.length < 2) {
|
|
1314
|
-
return true;
|
|
1315
|
-
}
|
|
1316
|
-
|
|
1317
|
-
let invalidHandlePlacement;
|
|
1318
|
-
|
|
1319
|
-
if (this._dragging) {
|
|
1320
|
-
invalidHandlePlacement = this._checkHandlesPencilMode(data, eventData);
|
|
1321
|
-
} else {
|
|
1322
|
-
invalidHandlePlacement = this._checkHandlesPolygonMode(data, eventData);
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
data.handles.invalidHandlePlacement = invalidHandlePlacement;
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
/**
|
|
1329
|
-
* Returns true if the proposed location of a new handle is invalid (in polygon mode).
|
|
1330
|
-
*
|
|
1331
|
-
* @private
|
|
1332
|
-
*
|
|
1333
|
-
* @param {Object} data - data object associated with the tool.
|
|
1334
|
-
* @param {Object} eventData The data assoicated with the event.
|
|
1335
|
-
* @returns {Boolean}
|
|
1336
|
-
*/
|
|
1337
|
-
_checkHandlesPolygonMode(data, eventData) {
|
|
1338
|
-
const config = this.configuration;
|
|
1339
|
-
const { element } = eventData;
|
|
1340
|
-
const mousePoint = config.mouseLocation.handles.start;
|
|
1341
|
-
const points = data.handles.points;
|
|
1342
|
-
let invalidHandlePlacement = false;
|
|
1343
|
-
|
|
1344
|
-
data.canComplete = false;
|
|
1345
|
-
|
|
1346
|
-
const mouseAtOriginHandle =
|
|
1347
|
-
this._isDistanceSmallerThanCompleteSpacingCanvas(
|
|
1348
|
-
element,
|
|
1349
|
-
points[0],
|
|
1350
|
-
mousePoint
|
|
1351
|
-
);
|
|
1352
|
-
|
|
1353
|
-
if (
|
|
1354
|
-
mouseAtOriginHandle &&
|
|
1355
|
-
!freehandIntersect.end(points) &&
|
|
1356
|
-
points.length > 2
|
|
1357
|
-
) {
|
|
1358
|
-
data.canComplete = true;
|
|
1359
|
-
invalidHandlePlacement = false;
|
|
1360
|
-
} else {
|
|
1361
|
-
invalidHandlePlacement = freehandIntersect.newHandle(mousePoint, points);
|
|
1362
|
-
}
|
|
1363
|
-
|
|
1364
|
-
return invalidHandlePlacement;
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
|
-
/**
|
|
1368
|
-
* Returns true if the proposed location of a new handle is invalid (in pencilMode).
|
|
1369
|
-
*
|
|
1370
|
-
* @private
|
|
1371
|
-
* @param {Object} data - data object associated with the tool.
|
|
1372
|
-
* @param {Object} eventData The data associated with the event.
|
|
1373
|
-
* @returns {Boolean}
|
|
1374
|
-
*/
|
|
1375
|
-
_checkHandlesPencilMode(data, eventData) {
|
|
1376
|
-
const config = this.configuration;
|
|
1377
|
-
const mousePoint = config.mouseLocation.handles.start;
|
|
1378
|
-
const points = data.handles.points;
|
|
1379
|
-
let invalidHandlePlacement = freehandIntersect.newHandle(
|
|
1380
|
-
mousePoint,
|
|
1381
|
-
points
|
|
1382
|
-
);
|
|
1383
|
-
|
|
1384
|
-
if (invalidHandlePlacement === false) {
|
|
1385
|
-
invalidHandlePlacement = this._invalidHandlePencilMode(data, eventData);
|
|
1386
|
-
}
|
|
1387
|
-
|
|
1388
|
-
return invalidHandlePlacement;
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
|
-
/**
|
|
1392
|
-
* Returns true if the mouse position is far enough from previous points (in pencilMode).
|
|
1393
|
-
*
|
|
1394
|
-
* @private
|
|
1395
|
-
* @param {Object} data - data object associated with the tool.
|
|
1396
|
-
* @param {Object} eventData The data associated with the event.
|
|
1397
|
-
* @returns {Boolean}
|
|
1398
|
-
*/
|
|
1399
|
-
_invalidHandlePencilMode(data, eventData) {
|
|
1400
|
-
const config = this.configuration;
|
|
1401
|
-
const { element } = eventData;
|
|
1402
|
-
const mousePoint = config.mouseLocation.handles.start;
|
|
1403
|
-
const points = data.handles.points;
|
|
1404
|
-
|
|
1405
|
-
const mouseAtOriginHandle =
|
|
1406
|
-
this._isDistanceSmallerThanCompleteSpacingCanvas(
|
|
1407
|
-
element,
|
|
1408
|
-
points[0],
|
|
1409
|
-
mousePoint
|
|
1410
|
-
);
|
|
1411
|
-
|
|
1412
|
-
if (mouseAtOriginHandle) {
|
|
1413
|
-
data.canComplete = true;
|
|
1414
|
-
|
|
1415
|
-
return false;
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
|
-
data.canComplete = false;
|
|
1419
|
-
|
|
1420
|
-
// Compare with all other handles appart from the last one
|
|
1421
|
-
for (let i = 1; i < points.length - 1; i++) {
|
|
1422
|
-
if (this._isDistanceSmallerThanSpacing(element, points[i], mousePoint)) {
|
|
1423
|
-
return true;
|
|
1424
|
-
}
|
|
1425
|
-
}
|
|
1426
|
-
|
|
1427
|
-
return false;
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
/**
|
|
1431
|
-
* Returns true if two points are closer than this.configuration.spacing.
|
|
1432
|
-
*
|
|
1433
|
-
* @private
|
|
1434
|
-
* @param {Object} element The element on which the roi is being drawn.
|
|
1435
|
-
* @param {Object} p1 The first point, in pixel space.
|
|
1436
|
-
* @param {Object} p2 The second point, in pixel space.
|
|
1437
|
-
* @returns {boolean} True if the distance is smaller than the
|
|
1438
|
-
* allowed canvas spacing.
|
|
1439
|
-
*/
|
|
1440
|
-
_isDistanceSmallerThanCompleteSpacingCanvas(element, p1, p2) {
|
|
1441
|
-
const p1Canvas = external.cornerstone.pixelToCanvas(element, p1);
|
|
1442
|
-
const p2Canvas = external.cornerstone.pixelToCanvas(element, p2);
|
|
1443
|
-
|
|
1444
|
-
let completeHandleRadius;
|
|
1445
|
-
|
|
1446
|
-
if (this._drawingInteractionType === "Mouse") {
|
|
1447
|
-
completeHandleRadius = this.configuration.completeHandleRadius;
|
|
1448
|
-
} else if (this._drawingInteractionType === "Touch") {
|
|
1449
|
-
completeHandleRadius = this.configuration.completeHandleRadiusTouch;
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
return this._compareDistanceToSpacing(
|
|
1453
|
-
element,
|
|
1454
|
-
p1Canvas,
|
|
1455
|
-
p2Canvas,
|
|
1456
|
-
"<",
|
|
1457
|
-
completeHandleRadius
|
|
1458
|
-
);
|
|
1459
|
-
}
|
|
1460
|
-
|
|
1461
|
-
/**
|
|
1462
|
-
* Returns true if two points are closer than this.configuration.spacing.
|
|
1463
|
-
*
|
|
1464
|
-
* @private
|
|
1465
|
-
* @param {Object} element The element on which the roi is being drawn.
|
|
1466
|
-
* @param {Object} p1 The first point, in pixel space.
|
|
1467
|
-
* @param {Object} p2 The second point, in pixel space.
|
|
1468
|
-
* @returns {boolean} True if the distance is smaller than the
|
|
1469
|
-
* allowed canvas spacing.
|
|
1470
|
-
*/
|
|
1471
|
-
_isDistanceSmallerThanSpacing(element, p1, p2) {
|
|
1472
|
-
return this._compareDistanceToSpacing(element, p1, p2, "<");
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
/**
|
|
1476
|
-
* Returns true if two points are farther than this.configuration.spacing.
|
|
1477
|
-
*
|
|
1478
|
-
* @private
|
|
1479
|
-
* @param {Object} element The element on which the roi is being drawn.
|
|
1480
|
-
* @param {Object} p1 The first point, in pixel space.
|
|
1481
|
-
* @param {Object} p2 The second point, in pixel space.
|
|
1482
|
-
* @returns {boolean} True if the distance is smaller than the
|
|
1483
|
-
* allowed canvas spacing.
|
|
1484
|
-
*/
|
|
1485
|
-
_isDistanceLargerThanSpacing(element, p1, p2) {
|
|
1486
|
-
return this._compareDistanceToSpacing(element, p1, p2, ">");
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
/**
|
|
1490
|
-
* Compares the distance between two points to this.configuration.spacing.
|
|
1491
|
-
*
|
|
1492
|
-
* @private
|
|
1493
|
-
* @param {Object} element The element on which the roi is being drawn.
|
|
1494
|
-
* @param {Object} p1 The first point, in pixel space.
|
|
1495
|
-
* @param {Object} p2 The second point, in pixel space.
|
|
1496
|
-
* @param {string} comparison The comparison to make.
|
|
1497
|
-
* @param {number} spacing The allowed canvas spacing
|
|
1498
|
-
* @returns {boolean} True if the distance is smaller than the
|
|
1499
|
-
* allowed canvas spacing.
|
|
1500
|
-
*/
|
|
1501
|
-
_compareDistanceToSpacing(
|
|
1502
|
-
element,
|
|
1503
|
-
p1,
|
|
1504
|
-
p2,
|
|
1505
|
-
comparison = ">",
|
|
1506
|
-
spacing = this.configuration.spacing
|
|
1507
|
-
) {
|
|
1508
|
-
if (comparison === ">") {
|
|
1509
|
-
return external.cornerstoneMath.point.distance(p1, p2) > spacing;
|
|
1510
|
-
}
|
|
1511
|
-
|
|
1512
|
-
return external.cornerstoneMath.point.distance(p1, p2) < spacing;
|
|
1513
|
-
}
|
|
1514
|
-
|
|
1515
|
-
/**
|
|
1516
|
-
* Adds drawing loop event listeners.
|
|
1517
|
-
*
|
|
1518
|
-
* @private
|
|
1519
|
-
* @param {Object} element - The viewport element to add event listeners to.
|
|
1520
|
-
* @param {string} interactionType - The interactionType used for the loop.
|
|
1521
|
-
* @modifies {element}
|
|
1522
|
-
* @returns {undefined}
|
|
1523
|
-
*/
|
|
1524
|
-
_activateDraw(element, interactionType = "Mouse") {
|
|
1525
|
-
this._drawing = true;
|
|
1526
|
-
this._drawingInteractionType = interactionType;
|
|
1527
|
-
|
|
1528
|
-
state.isMultiPartToolActive = true;
|
|
1529
|
-
// hideToolCursor(this.element);
|
|
1530
|
-
|
|
1531
|
-
// Polygonal Mode
|
|
1532
|
-
element.addEventListener(EVENTS.MOUSE_DOWN, this._drawingMouseDownCallback);
|
|
1533
|
-
element.addEventListener(EVENTS.MOUSE_MOVE, this._drawingMouseMoveCallback);
|
|
1534
|
-
element.addEventListener(
|
|
1535
|
-
EVENTS.MOUSE_DOUBLE_CLICK,
|
|
1536
|
-
this._drawingMouseDoubleClickCallback
|
|
1537
|
-
);
|
|
1538
|
-
|
|
1539
|
-
// Drag/Pencil Mode
|
|
1540
|
-
element.addEventListener(EVENTS.MOUSE_DRAG, this._drawingMouseDragCallback);
|
|
1541
|
-
element.addEventListener(EVENTS.MOUSE_UP, this._drawingMouseUpCallback);
|
|
1542
|
-
|
|
1543
|
-
// Touch
|
|
1544
|
-
element.addEventListener(
|
|
1545
|
-
EVENTS.TOUCH_START,
|
|
1546
|
-
this._drawingMouseMoveCallback
|
|
1547
|
-
);
|
|
1548
|
-
element.addEventListener(
|
|
1549
|
-
EVENTS.TOUCH_START,
|
|
1550
|
-
this._drawingTouchStartCallback
|
|
1551
|
-
);
|
|
1552
|
-
|
|
1553
|
-
element.addEventListener(EVENTS.TOUCH_DRAG, this._drawingTouchDragCallback);
|
|
1554
|
-
element.addEventListener(EVENTS.TOUCH_END, this._drawingMouseUpCallback);
|
|
1555
|
-
element.addEventListener(
|
|
1556
|
-
EVENTS.DOUBLE_TAP,
|
|
1557
|
-
this._drawingDoubleTapClickCallback
|
|
1558
|
-
);
|
|
1559
|
-
|
|
1560
|
-
external.cornerstone.updateImage(element);
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
|
-
/**
|
|
1564
|
-
* Removes drawing loop event listeners.
|
|
1565
|
-
*
|
|
1566
|
-
* @private
|
|
1567
|
-
* @param {Object} element - The viewport element to add event listeners to.
|
|
1568
|
-
* @modifies {element}
|
|
1569
|
-
* @returns {undefined}
|
|
1570
|
-
*/
|
|
1571
|
-
_deactivateDraw(element) {
|
|
1572
|
-
this._drawing = false;
|
|
1573
|
-
state.isMultiPartToolActive = false;
|
|
1574
|
-
this._activeDrawingToolReference = null;
|
|
1575
|
-
this._drawingInteractionType = null;
|
|
1576
|
-
// setToolCursor(this.element, this.svgCursor);
|
|
1577
|
-
|
|
1578
|
-
element.removeEventListener(
|
|
1579
|
-
EVENTS.MOUSE_DOWN,
|
|
1580
|
-
this._drawingMouseDownCallback
|
|
1581
|
-
);
|
|
1582
|
-
element.removeEventListener(
|
|
1583
|
-
EVENTS.MOUSE_MOVE,
|
|
1584
|
-
this._drawingMouseMoveCallback
|
|
1585
|
-
);
|
|
1586
|
-
element.removeEventListener(
|
|
1587
|
-
EVENTS.MOUSE_DOUBLE_CLICK,
|
|
1588
|
-
this._drawingMouseDoubleClickCallback
|
|
1589
|
-
);
|
|
1590
|
-
element.removeEventListener(
|
|
1591
|
-
EVENTS.MOUSE_DRAG,
|
|
1592
|
-
this._drawingMouseDragCallback
|
|
1593
|
-
);
|
|
1594
|
-
element.removeEventListener(EVENTS.MOUSE_UP, this._drawingMouseUpCallback);
|
|
1595
|
-
|
|
1596
|
-
// Touch
|
|
1597
|
-
element.removeEventListener(
|
|
1598
|
-
EVENTS.TOUCH_START,
|
|
1599
|
-
this._drawingTouchStartCallback
|
|
1600
|
-
);
|
|
1601
|
-
element.removeEventListener(
|
|
1602
|
-
EVENTS.TOUCH_DRAG,
|
|
1603
|
-
this._drawingTouchDragCallback
|
|
1604
|
-
);
|
|
1605
|
-
element.removeEventListener(
|
|
1606
|
-
EVENTS.TOUCH_START,
|
|
1607
|
-
this._drawingMouseMoveCallback
|
|
1608
|
-
);
|
|
1609
|
-
element.removeEventListener(EVENTS.TOUCH_END, this._drawingMouseUpCallback);
|
|
1610
|
-
|
|
1611
|
-
external.cornerstone.updateImage(element);
|
|
1612
|
-
}
|
|
1613
|
-
|
|
1614
|
-
/**
|
|
1615
|
-
* Adds modify loop event listeners.
|
|
1616
|
-
*
|
|
1617
|
-
* @private
|
|
1618
|
-
* @param {Object} element - The viewport element to add event listeners to.
|
|
1619
|
-
* @modifies {element}
|
|
1620
|
-
* @returns {undefined}
|
|
1621
|
-
*/
|
|
1622
|
-
_activateModify(element) {
|
|
1623
|
-
state.isToolLocked = true;
|
|
1624
|
-
|
|
1625
|
-
element.addEventListener(EVENTS.MOUSE_UP, this._editMouseUpCallback);
|
|
1626
|
-
element.addEventListener(EVENTS.MOUSE_DRAG, this._editMouseDragCallback);
|
|
1627
|
-
element.addEventListener(EVENTS.MOUSE_CLICK, this._editMouseUpCallback);
|
|
1628
|
-
|
|
1629
|
-
element.addEventListener(EVENTS.TOUCH_END, this._editMouseUpCallback);
|
|
1630
|
-
element.addEventListener(EVENTS.TOUCH_DRAG, this._editTouchDragCallback);
|
|
1631
|
-
|
|
1632
|
-
external.cornerstone.updateImage(element);
|
|
1633
|
-
}
|
|
1634
|
-
|
|
1635
|
-
/**
|
|
1636
|
-
* Removes modify loop event listeners.
|
|
1637
|
-
*
|
|
1638
|
-
* @private
|
|
1639
|
-
* @param {Object} element - The viewport element to add event listeners to.
|
|
1640
|
-
* @modifies {element}
|
|
1641
|
-
* @returns {undefined}
|
|
1642
|
-
*/
|
|
1643
|
-
_deactivateModify(element) {
|
|
1644
|
-
state.isToolLocked = false;
|
|
1645
|
-
|
|
1646
|
-
element.removeEventListener(EVENTS.MOUSE_UP, this._editMouseUpCallback);
|
|
1647
|
-
element.removeEventListener(EVENTS.MOUSE_DRAG, this._editMouseDragCallback);
|
|
1648
|
-
element.removeEventListener(EVENTS.MOUSE_CLICK, this._editMouseUpCallback);
|
|
1649
|
-
|
|
1650
|
-
element.removeEventListener(EVENTS.TOUCH_END, this._editMouseUpCallback);
|
|
1651
|
-
element.removeEventListener(EVENTS.TOUCH_DRAG, this._editTouchDragCallback);
|
|
1652
|
-
|
|
1653
|
-
external.cornerstone.updateImage(element);
|
|
1654
|
-
}
|
|
1655
|
-
|
|
1656
|
-
passiveCallback(element) {
|
|
1657
|
-
this._closeToolIfDrawing(element);
|
|
1658
|
-
}
|
|
1659
|
-
|
|
1660
|
-
enabledCallback(element) {
|
|
1661
|
-
this._closeToolIfDrawing(element);
|
|
1662
|
-
}
|
|
1663
|
-
|
|
1664
|
-
disabledCallback(element) {
|
|
1665
|
-
this._closeToolIfDrawing(element);
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
_closeToolIfDrawing(element) {
|
|
1669
|
-
if (this._drawing) {
|
|
1670
|
-
// Actively drawing but changed mode.
|
|
1671
|
-
const config = this.configuration;
|
|
1672
|
-
const lastHandlePlaced = config.currentHandle;
|
|
1673
|
-
|
|
1674
|
-
this._endDrawing(element, lastHandlePlaced);
|
|
1675
|
-
external.cornerstone.updateImage(element);
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1678
|
-
|
|
1679
|
-
/**
|
|
1680
|
-
* Fire MEASUREMENT_MODIFIED event on provided element
|
|
1681
|
-
* @param {any} element which freehand data has been modified
|
|
1682
|
-
* @param {any} measurementData the measurment data
|
|
1683
|
-
* @returns {void}
|
|
1684
|
-
*/
|
|
1685
|
-
fireModifiedEvent(element, measurementData) {
|
|
1686
|
-
const eventType = EVENTS.MEASUREMENT_MODIFIED;
|
|
1687
|
-
const eventData = {
|
|
1688
|
-
toolName: this.name,
|
|
1689
|
-
element,
|
|
1690
|
-
measurementData
|
|
1691
|
-
};
|
|
1692
|
-
|
|
1693
|
-
triggerEvent(element, eventType, eventData);
|
|
1694
|
-
}
|
|
1695
|
-
|
|
1696
|
-
fireCompletedEvent(element, measurementData) {
|
|
1697
|
-
const eventType = EVENTS.MEASUREMENT_COMPLETED;
|
|
1698
|
-
const eventData = {
|
|
1699
|
-
toolName: this.name,
|
|
1700
|
-
element,
|
|
1701
|
-
measurementData
|
|
1702
|
-
};
|
|
1703
|
-
|
|
1704
|
-
triggerEvent(element, eventType, eventData);
|
|
1705
|
-
}
|
|
1706
|
-
|
|
1707
|
-
// ===================================================================
|
|
1708
|
-
// Public Configuration API. .
|
|
1709
|
-
// ===================================================================
|
|
1710
|
-
|
|
1711
|
-
get spacing() {
|
|
1712
|
-
return this.configuration.spacing;
|
|
1713
|
-
}
|
|
1714
|
-
|
|
1715
|
-
set spacing(value) {
|
|
1716
|
-
if (typeof value !== "number") {
|
|
1717
|
-
throw new Error(
|
|
1718
|
-
"Attempting to set freehand spacing to a value other than a number."
|
|
1719
|
-
);
|
|
1720
|
-
}
|
|
1721
|
-
|
|
1722
|
-
this.configuration.spacing = value;
|
|
1723
|
-
external.cornerstone.updateImage(this.element);
|
|
1724
|
-
}
|
|
1725
|
-
|
|
1726
|
-
get activeHandleRadius() {
|
|
1727
|
-
return this.configuration.activeHandleRadius;
|
|
1728
|
-
}
|
|
1729
|
-
|
|
1730
|
-
set activeHandleRadius(value) {
|
|
1731
|
-
if (typeof value !== "number") {
|
|
1732
|
-
throw new Error(
|
|
1733
|
-
"Attempting to set freehand activeHandleRadius to a value other than a number."
|
|
1734
|
-
);
|
|
1735
|
-
}
|
|
1736
|
-
|
|
1737
|
-
this.configuration.activeHandleRadius = value;
|
|
1738
|
-
external.cornerstone.updateImage(this.element);
|
|
1739
|
-
}
|
|
1740
|
-
|
|
1741
|
-
get completeHandleRadius() {
|
|
1742
|
-
return this.configuration.completeHandleRadius;
|
|
1743
|
-
}
|
|
1744
|
-
|
|
1745
|
-
set completeHandleRadius(value) {
|
|
1746
|
-
if (typeof value !== "number") {
|
|
1747
|
-
throw new Error(
|
|
1748
|
-
"Attempting to set freehand completeHandleRadius to a value other than a number."
|
|
1749
|
-
);
|
|
1750
|
-
}
|
|
1751
|
-
|
|
1752
|
-
this.configuration.completeHandleRadius = value;
|
|
1753
|
-
external.cornerstone.updateImage(this.element);
|
|
1754
|
-
}
|
|
1755
|
-
|
|
1756
|
-
get alwaysShowHandles() {
|
|
1757
|
-
return this.configuration.alwaysShowHandles;
|
|
1758
|
-
}
|
|
1759
|
-
|
|
1760
|
-
set alwaysShowHandles(value) {
|
|
1761
|
-
if (typeof value !== "boolean") {
|
|
1762
|
-
throw new Error(
|
|
1763
|
-
"Attempting to set freehand alwaysShowHandles to a value other than a boolean."
|
|
1764
|
-
);
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
this.configuration.alwaysShowHandles = value;
|
|
1768
|
-
external.cornerstone.updateImage(this.element);
|
|
1769
|
-
}
|
|
1770
|
-
|
|
1771
|
-
get invalidColor() {
|
|
1772
|
-
return this.configuration.invalidColor;
|
|
1773
|
-
}
|
|
1774
|
-
|
|
1775
|
-
set invalidColor(value) {
|
|
1776
|
-
/*
|
|
1777
|
-
It'd be easy to check if the color was e.g. a valid rgba color. However
|
|
1778
|
-
it'd be difficult to check if the color was a named CSS color without
|
|
1779
|
-
bloating the library, so we don't. If the canvas can't intepret the color
|
|
1780
|
-
it'll show up grey.
|
|
1781
|
-
*/
|
|
1782
|
-
|
|
1783
|
-
this.configuration.invalidColor = value;
|
|
1784
|
-
external.cornerstone.updateImage(this.element);
|
|
1785
|
-
}
|
|
1786
|
-
|
|
1787
|
-
/**
|
|
1788
|
-
* Ends the active drawing loop and removes the polygon.
|
|
1789
|
-
*
|
|
1790
|
-
* @public
|
|
1791
|
-
* @param {Object} element - The element on which the roi is being drawn.
|
|
1792
|
-
* @returns {null}
|
|
1793
|
-
*/
|
|
1794
|
-
cancelDrawing(element) {
|
|
1795
|
-
if (!this._drawing) {
|
|
1796
|
-
return;
|
|
1797
|
-
}
|
|
1798
|
-
const toolState = getToolState(element, this.name);
|
|
1799
|
-
|
|
1800
|
-
const config = this.configuration;
|
|
1801
|
-
|
|
1802
|
-
const data = toolState.data[config.currentTool];
|
|
1803
|
-
|
|
1804
|
-
data.active = false;
|
|
1805
|
-
data.highlight = false;
|
|
1806
|
-
data.handles.invalidHandlePlacement = false;
|
|
1807
|
-
|
|
1808
|
-
// Reset the current handle
|
|
1809
|
-
config.currentHandle = 0;
|
|
1810
|
-
config.currentTool = -1;
|
|
1811
|
-
data.canComplete = false;
|
|
1812
|
-
|
|
1813
|
-
removeToolState(element, this.name, data);
|
|
1814
|
-
|
|
1815
|
-
this._deactivateDraw(element);
|
|
1816
|
-
|
|
1817
|
-
external.cornerstone.updateImage(element);
|
|
1818
|
-
}
|
|
1819
|
-
|
|
1820
|
-
/**
|
|
1821
|
-
* New image event handler.
|
|
1822
|
-
*
|
|
1823
|
-
* @public
|
|
1824
|
-
* @param {Object} evt The event.
|
|
1825
|
-
* @returns {null}
|
|
1826
|
-
*/
|
|
1827
|
-
newImageCallback(evt) {
|
|
1828
|
-
const config = this.configuration;
|
|
1829
|
-
|
|
1830
|
-
if (!(this._drawing && this._activeDrawingToolReference)) {
|
|
1831
|
-
return;
|
|
1832
|
-
}
|
|
1833
|
-
|
|
1834
|
-
// Actively drawing but scrolled to different image.
|
|
1835
|
-
|
|
1836
|
-
const element = evt.detail.element;
|
|
1837
|
-
const data = this._activeDrawingToolReference;
|
|
1838
|
-
|
|
1839
|
-
data.active = false;
|
|
1840
|
-
data.highlight = false;
|
|
1841
|
-
data.handles.invalidHandlePlacement = false;
|
|
1842
|
-
|
|
1843
|
-
// Connect the end handle to the origin handle
|
|
1844
|
-
const points = data.handles.points;
|
|
1845
|
-
|
|
1846
|
-
points[config.currentHandle - 1].lines.push(points[0]);
|
|
1847
|
-
|
|
1848
|
-
// Reset the current handle
|
|
1849
|
-
config.currentHandle = 0;
|
|
1850
|
-
config.currentTool = -1;
|
|
1851
|
-
data.canComplete = false;
|
|
1852
|
-
|
|
1853
|
-
this._deactivateDraw(element);
|
|
1854
|
-
|
|
1855
|
-
external.cornerstone.updateImage(element);
|
|
1856
|
-
}
|
|
1857
|
-
}
|
|
1858
|
-
|
|
1859
|
-
function defaultFreehandConfiguration() {
|
|
1860
|
-
return {
|
|
1861
|
-
mouseLocation: {
|
|
1862
|
-
handles: {
|
|
1863
|
-
start: {
|
|
1864
|
-
highlight: false,
|
|
1865
|
-
active: false
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
|
-
},
|
|
1869
|
-
spacing: 1,
|
|
1870
|
-
activeHandleRadius: 3,
|
|
1871
|
-
completeHandleRadius: 6,
|
|
1872
|
-
completeHandleRadiusTouch: 28,
|
|
1873
|
-
alwaysShowHandles: false,
|
|
1874
|
-
invalidColor: "#FFFF00",
|
|
1875
|
-
currentHandle: 0,
|
|
1876
|
-
currentTool: -1
|
|
1877
|
-
};
|
|
1878
|
-
}
|
|
1879
|
-
|
|
1880
|
-
function preventPropagation(evt) {
|
|
1881
|
-
evt.stopImmediatePropagation();
|
|
1882
|
-
evt.stopPropagation();
|
|
1883
|
-
evt.preventDefault();
|
|
1884
|
-
}
|