@cornerstonejs/tools 3.31.13 → 3.32.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/esm/enums/Events.d.ts +2 -0
- package/dist/esm/enums/Events.js +2 -0
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +2 -2
- package/dist/esm/tools/VolumeCroppingControlTool.d.ts +91 -0
- package/dist/esm/tools/VolumeCroppingControlTool.js +1208 -0
- package/dist/esm/tools/VolumeCroppingTool.d.ts +96 -0
- package/dist/esm/tools/VolumeCroppingTool.js +1064 -0
- package/dist/esm/tools/index.d.ts +3 -1
- package/dist/esm/tools/index.js +3 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,1208 @@
|
|
|
1
|
+
import { vec2, vec3 } from 'gl-matrix';
|
|
2
|
+
import vtkMath from '@kitware/vtk.js/Common/Core/Math';
|
|
3
|
+
import { AnnotationTool } from './base';
|
|
4
|
+
import { getRenderingEngine, getEnabledElementByIds, getEnabledElement, utilities as csUtils, Enums, CONSTANTS, triggerEvent, eventTarget, } from '@cornerstonejs/core';
|
|
5
|
+
import { getToolGroup, getToolGroupForViewport, } from '../store/ToolGroupManager';
|
|
6
|
+
import { addAnnotation, getAnnotations, removeAnnotation, } from '../stateManagement/annotation/annotationState';
|
|
7
|
+
import { drawCircle as drawCircleSvg, drawLine as drawLineSvg, } from '../drawingSvg';
|
|
8
|
+
import { state } from '../store/state';
|
|
9
|
+
import { Events } from '../enums';
|
|
10
|
+
import { getViewportIdsWithToolToRender } from '../utilities/viewportFilters';
|
|
11
|
+
import { resetElementCursor, hideElementCursor, } from '../cursors/elementCursor';
|
|
12
|
+
import liangBarksyClip from '../utilities/math/vec2/liangBarksyClip';
|
|
13
|
+
import * as lineSegment from '../utilities/math/line';
|
|
14
|
+
import { isAnnotationLocked } from '../stateManagement/annotation/annotationLocking';
|
|
15
|
+
import triggerAnnotationRenderForViewportIds from '../utilities/triggerAnnotationRenderForViewportIds';
|
|
16
|
+
const { RENDERING_DEFAULTS } = CONSTANTS;
|
|
17
|
+
function defaultReferenceLineColor() {
|
|
18
|
+
return 'rgb(0, 200, 0)';
|
|
19
|
+
}
|
|
20
|
+
function defaultReferenceLineControllable() {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
const OPERATION = {
|
|
24
|
+
DRAG: 1,
|
|
25
|
+
ROTATE: 2,
|
|
26
|
+
SLAB: 3,
|
|
27
|
+
};
|
|
28
|
+
class VolumeCroppingControlTool extends AnnotationTool {
|
|
29
|
+
constructor(toolProps = {}, defaultToolProps = {
|
|
30
|
+
supportedInteractionTypes: ['Mouse'],
|
|
31
|
+
configuration: {
|
|
32
|
+
viewportIndicators: false,
|
|
33
|
+
viewportIndicatorsConfig: {
|
|
34
|
+
radius: 5,
|
|
35
|
+
x: null,
|
|
36
|
+
y: null,
|
|
37
|
+
},
|
|
38
|
+
extendReferenceLines: true,
|
|
39
|
+
initialCropFactor: 0.2,
|
|
40
|
+
mobile: {
|
|
41
|
+
enabled: false,
|
|
42
|
+
opacity: 0.8,
|
|
43
|
+
},
|
|
44
|
+
lineColors: {
|
|
45
|
+
AXIAL: [1.0, 0.0, 0.0],
|
|
46
|
+
CORONAL: [0.0, 1.0, 0.0],
|
|
47
|
+
SAGITTAL: [1.0, 1.0, 0.0],
|
|
48
|
+
UNKNOWN: [0.0, 0.0, 1.0],
|
|
49
|
+
},
|
|
50
|
+
lineWidth: 1.5,
|
|
51
|
+
lineWidthActive: 2.5,
|
|
52
|
+
},
|
|
53
|
+
}) {
|
|
54
|
+
super(toolProps, defaultToolProps);
|
|
55
|
+
this._virtualAnnotations = [];
|
|
56
|
+
this.sphereStates = [];
|
|
57
|
+
this.draggingSphereIndex = null;
|
|
58
|
+
this.toolCenter = [0, 0, 0];
|
|
59
|
+
this.toolCenterMin = [0, 0, 0];
|
|
60
|
+
this.toolCenterMax = [0, 0, 0];
|
|
61
|
+
this.initializeViewport = ({ renderingEngineId, viewportId, }) => {
|
|
62
|
+
if (!renderingEngineId || !viewportId) {
|
|
63
|
+
console.warn('VolumeCroppingControlTool: Missing renderingEngineId or viewportId');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const enabledElement = getEnabledElementByIds(viewportId, renderingEngineId);
|
|
67
|
+
if (!enabledElement) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const { viewport } = enabledElement;
|
|
71
|
+
this._updateToolCentersFromViewport(viewport);
|
|
72
|
+
const { element } = viewport;
|
|
73
|
+
const { position, focalPoint, viewPlaneNormal } = viewport.getCamera();
|
|
74
|
+
let annotations = this._getAnnotations(enabledElement);
|
|
75
|
+
annotations = this.filterInteractableAnnotationsForElement(element, annotations);
|
|
76
|
+
if (annotations?.length) {
|
|
77
|
+
removeAnnotation(annotations[0].annotationUID);
|
|
78
|
+
}
|
|
79
|
+
const orientation = this._getOrientationFromNormal(viewport.getCamera().viewPlaneNormal);
|
|
80
|
+
const annotation = {
|
|
81
|
+
highlighted: false,
|
|
82
|
+
metadata: {
|
|
83
|
+
cameraPosition: [...position],
|
|
84
|
+
cameraFocalPoint: [...focalPoint],
|
|
85
|
+
toolName: this.getToolName(),
|
|
86
|
+
},
|
|
87
|
+
data: {
|
|
88
|
+
handles: {
|
|
89
|
+
toolCenter: this.toolCenter,
|
|
90
|
+
toolCenterMin: this.toolCenterMin,
|
|
91
|
+
toolCenterMax: this.toolCenterMax,
|
|
92
|
+
},
|
|
93
|
+
activeOperation: null,
|
|
94
|
+
activeViewportIds: [],
|
|
95
|
+
viewportId,
|
|
96
|
+
referenceLines: [],
|
|
97
|
+
orientation,
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
addAnnotation(annotation, element);
|
|
101
|
+
return {
|
|
102
|
+
normal: viewPlaneNormal,
|
|
103
|
+
point: viewport.canvasToWorld([100, 100]),
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
this._getViewportsInfo = () => {
|
|
107
|
+
const viewports = getToolGroup(this.toolGroupId).viewportsInfo;
|
|
108
|
+
return viewports;
|
|
109
|
+
};
|
|
110
|
+
this.resetCroppingSpheres = () => {
|
|
111
|
+
const viewportsInfo = this._getViewportsInfo();
|
|
112
|
+
for (const viewportInfo of viewportsInfo) {
|
|
113
|
+
const { viewportId, renderingEngineId } = viewportInfo;
|
|
114
|
+
const enabledElement = getEnabledElementByIds(viewportId, renderingEngineId);
|
|
115
|
+
const viewport = enabledElement.viewport;
|
|
116
|
+
const resetPan = true;
|
|
117
|
+
const resetZoom = true;
|
|
118
|
+
const resetToCenter = true;
|
|
119
|
+
const resetRotation = true;
|
|
120
|
+
const suppressEvents = true;
|
|
121
|
+
viewport.resetCamera({
|
|
122
|
+
resetPan,
|
|
123
|
+
resetZoom,
|
|
124
|
+
resetToCenter,
|
|
125
|
+
resetRotation,
|
|
126
|
+
suppressEvents,
|
|
127
|
+
});
|
|
128
|
+
viewport.resetSlabThickness();
|
|
129
|
+
const { element } = viewport;
|
|
130
|
+
let annotations = this._getAnnotations(enabledElement);
|
|
131
|
+
annotations = this.filterInteractableAnnotationsForElement(element, annotations);
|
|
132
|
+
if (annotations.length) {
|
|
133
|
+
removeAnnotation(annotations[0].annotationUID);
|
|
134
|
+
}
|
|
135
|
+
viewport.render();
|
|
136
|
+
}
|
|
137
|
+
this._computeToolCenter(viewportsInfo);
|
|
138
|
+
};
|
|
139
|
+
this.computeToolCenter = () => {
|
|
140
|
+
const viewportsInfo = this._getViewportsInfo();
|
|
141
|
+
};
|
|
142
|
+
this._computeToolCenter = (viewportsInfo) => {
|
|
143
|
+
if (!viewportsInfo || !viewportsInfo[0]) {
|
|
144
|
+
console.warn(' _computeToolCenter : No valid viewportsInfo for computeToolCenter.');
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const orientationIds = ['AXIAL', 'CORONAL', 'SAGITTAL'];
|
|
148
|
+
const presentOrientations = viewportsInfo
|
|
149
|
+
.map((vp) => {
|
|
150
|
+
if (vp.renderingEngineId) {
|
|
151
|
+
const renderingEngine = getRenderingEngine(vp.renderingEngineId);
|
|
152
|
+
const viewport = renderingEngine.getViewport(vp.viewportId);
|
|
153
|
+
if (viewport && viewport.getCamera) {
|
|
154
|
+
const orientation = this._getOrientationFromNormal(viewport.getCamera().viewPlaneNormal);
|
|
155
|
+
if (orientation) {
|
|
156
|
+
return orientation;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
})
|
|
162
|
+
.filter(Boolean);
|
|
163
|
+
const missingOrientation = orientationIds.find((id) => !presentOrientations.includes(id));
|
|
164
|
+
const presentNormals = [];
|
|
165
|
+
const presentCenters = [];
|
|
166
|
+
const presentViewportInfos = viewportsInfo.filter((vp) => {
|
|
167
|
+
let orientation = null;
|
|
168
|
+
if (vp.renderingEngineId) {
|
|
169
|
+
const renderingEngine = getRenderingEngine(vp.renderingEngineId);
|
|
170
|
+
const viewport = renderingEngine.getViewport(vp.viewportId);
|
|
171
|
+
if (viewport && viewport.getCamera) {
|
|
172
|
+
orientation = this._getOrientationFromNormal(viewport.getCamera().viewPlaneNormal);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return orientation && orientationIds.includes(orientation);
|
|
176
|
+
});
|
|
177
|
+
presentViewportInfos.forEach((vpInfo) => {
|
|
178
|
+
const { normal, point } = this.initializeViewport(vpInfo);
|
|
179
|
+
presentNormals.push(normal);
|
|
180
|
+
presentCenters.push(point);
|
|
181
|
+
});
|
|
182
|
+
if (presentViewportInfos.length === 2 && missingOrientation) {
|
|
183
|
+
const virtualNormal = [0, 0, 0];
|
|
184
|
+
vec3.cross(virtualNormal, presentNormals[0], presentNormals[1]);
|
|
185
|
+
vec3.normalize(virtualNormal, virtualNormal);
|
|
186
|
+
const virtualCenter = [
|
|
187
|
+
(presentCenters[0][0] + presentCenters[1][0]) / 2,
|
|
188
|
+
(presentCenters[0][1] + presentCenters[1][1]) / 2,
|
|
189
|
+
(presentCenters[0][2] + presentCenters[1][2]) / 2,
|
|
190
|
+
];
|
|
191
|
+
const orientation = null;
|
|
192
|
+
const virtualAnnotation = {
|
|
193
|
+
highlighted: false,
|
|
194
|
+
metadata: {
|
|
195
|
+
cameraPosition: [...virtualCenter],
|
|
196
|
+
cameraFocalPoint: [...virtualCenter],
|
|
197
|
+
toolName: this.getToolName(),
|
|
198
|
+
},
|
|
199
|
+
data: {
|
|
200
|
+
handles: {
|
|
201
|
+
activeOperation: null,
|
|
202
|
+
toolCenter: this.toolCenter,
|
|
203
|
+
toolCenterMin: this.toolCenterMin,
|
|
204
|
+
toolCenterMax: this.toolCenterMax,
|
|
205
|
+
},
|
|
206
|
+
activeViewportIds: [],
|
|
207
|
+
viewportId: missingOrientation,
|
|
208
|
+
referenceLines: [],
|
|
209
|
+
orientation,
|
|
210
|
+
},
|
|
211
|
+
isVirtual: true,
|
|
212
|
+
virtualNormal,
|
|
213
|
+
};
|
|
214
|
+
this._virtualAnnotations = [virtualAnnotation];
|
|
215
|
+
}
|
|
216
|
+
else if (presentViewportInfos.length === 1) {
|
|
217
|
+
let presentOrientation = null;
|
|
218
|
+
const vpInfo = presentViewportInfos[0];
|
|
219
|
+
if (vpInfo.renderingEngineId) {
|
|
220
|
+
const renderingEngine = getRenderingEngine(vpInfo.renderingEngineId);
|
|
221
|
+
const viewport = renderingEngine.getViewport(vpInfo.viewportId);
|
|
222
|
+
if (viewport && viewport.getCamera) {
|
|
223
|
+
presentOrientation = this._getOrientationFromNormal(viewport.getCamera().viewPlaneNormal);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const presentCenter = presentCenters[0];
|
|
227
|
+
const canonicalNormals = {
|
|
228
|
+
AXIAL: [0, 0, 1],
|
|
229
|
+
CORONAL: [0, 1, 0],
|
|
230
|
+
SAGITTAL: [1, 0, 0],
|
|
231
|
+
};
|
|
232
|
+
const missingIds = orientationIds.filter((id) => id !== presentOrientation);
|
|
233
|
+
const virtualAnnotations = missingIds.map((orientation) => {
|
|
234
|
+
const normal = canonicalNormals[orientation];
|
|
235
|
+
const virtualAnnotation = {
|
|
236
|
+
highlighted: false,
|
|
237
|
+
metadata: {
|
|
238
|
+
cameraPosition: [...presentCenter],
|
|
239
|
+
cameraFocalPoint: [...presentCenter],
|
|
240
|
+
toolName: this.getToolName(),
|
|
241
|
+
},
|
|
242
|
+
data: {
|
|
243
|
+
handles: {
|
|
244
|
+
activeOperation: null,
|
|
245
|
+
toolCenter: this.toolCenter,
|
|
246
|
+
toolCenterMin: this.toolCenterMin,
|
|
247
|
+
toolCenterMax: this.toolCenterMax,
|
|
248
|
+
},
|
|
249
|
+
activeViewportIds: [],
|
|
250
|
+
viewportId: orientation,
|
|
251
|
+
referenceLines: [],
|
|
252
|
+
orientation,
|
|
253
|
+
},
|
|
254
|
+
isVirtual: true,
|
|
255
|
+
virtualNormal: normal,
|
|
256
|
+
};
|
|
257
|
+
return virtualAnnotation;
|
|
258
|
+
});
|
|
259
|
+
this._virtualAnnotations = virtualAnnotations;
|
|
260
|
+
}
|
|
261
|
+
if (viewportsInfo && viewportsInfo.length) {
|
|
262
|
+
triggerAnnotationRenderForViewportIds(viewportsInfo.map(({ viewportId }) => viewportId));
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
this.cancel = () => {
|
|
266
|
+
console.log('Not implemented yet');
|
|
267
|
+
};
|
|
268
|
+
this.isPointNearTool = (element, annotation, canvasCoords, proximity) => {
|
|
269
|
+
if (this._pointNearTool(element, annotation, canvasCoords, 6)) {
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
return false;
|
|
273
|
+
};
|
|
274
|
+
this.toolSelectedCallback = (evt, annotation, interactionType) => {
|
|
275
|
+
const eventDetail = evt.detail;
|
|
276
|
+
const { element } = eventDetail;
|
|
277
|
+
annotation.highlighted = true;
|
|
278
|
+
this._activateModify(element);
|
|
279
|
+
hideElementCursor(element);
|
|
280
|
+
evt.preventDefault();
|
|
281
|
+
};
|
|
282
|
+
this.onResetCamera = (evt) => {
|
|
283
|
+
this.resetCroppingSpheres();
|
|
284
|
+
};
|
|
285
|
+
this.mouseMoveCallback = (evt, filteredToolAnnotations) => {
|
|
286
|
+
if (!filteredToolAnnotations) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const { element, currentPoints } = evt.detail;
|
|
290
|
+
const canvasCoords = currentPoints.canvas;
|
|
291
|
+
let imageNeedsUpdate = false;
|
|
292
|
+
for (let i = 0; i < filteredToolAnnotations.length; i++) {
|
|
293
|
+
const annotation = filteredToolAnnotations[i];
|
|
294
|
+
if (isAnnotationLocked(annotation.annotationUID)) {
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
const { data, highlighted } = annotation;
|
|
298
|
+
if (!data.handles) {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
const previousActiveOperation = data.handles.activeOperation;
|
|
302
|
+
const previousActiveViewportIds = data.activeViewportIds && data.activeViewportIds.length > 0
|
|
303
|
+
? [...data.activeViewportIds]
|
|
304
|
+
: [];
|
|
305
|
+
data.activeViewportIds = [];
|
|
306
|
+
let near = false;
|
|
307
|
+
near = this._pointNearTool(element, annotation, canvasCoords, 6);
|
|
308
|
+
const nearToolAndNotMarkedActive = near && !highlighted;
|
|
309
|
+
const notNearToolAndMarkedActive = !near && highlighted;
|
|
310
|
+
if (nearToolAndNotMarkedActive || notNearToolAndMarkedActive) {
|
|
311
|
+
annotation.highlighted = !highlighted;
|
|
312
|
+
imageNeedsUpdate = true;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return imageNeedsUpdate;
|
|
316
|
+
};
|
|
317
|
+
this.filterInteractableAnnotationsForElement = (element, annotations) => {
|
|
318
|
+
if (!annotations || !annotations.length) {
|
|
319
|
+
return [];
|
|
320
|
+
}
|
|
321
|
+
const enabledElement = getEnabledElement(element);
|
|
322
|
+
let orientation = null;
|
|
323
|
+
if (enabledElement.viewport && enabledElement.viewport.getCamera) {
|
|
324
|
+
orientation = this._getOrientationFromNormal(enabledElement.viewport.getCamera().viewPlaneNormal);
|
|
325
|
+
}
|
|
326
|
+
const filtered = annotations.filter((annotation) => {
|
|
327
|
+
if (annotation.isVirtual) {
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
if (annotation.data.orientation &&
|
|
331
|
+
orientation &&
|
|
332
|
+
annotation.data.orientation === orientation) {
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
return false;
|
|
336
|
+
});
|
|
337
|
+
return filtered;
|
|
338
|
+
};
|
|
339
|
+
this.renderAnnotation = (enabledElement, svgDrawingHelper) => {
|
|
340
|
+
function lineIntersection2D(p1, p2, q1, q2) {
|
|
341
|
+
const s1_x = p2[0] - p1[0];
|
|
342
|
+
const s1_y = p2[1] - p1[1];
|
|
343
|
+
const s2_x = q2[0] - q1[0];
|
|
344
|
+
const s2_y = q2[1] - q1[1];
|
|
345
|
+
const denom = -s2_x * s1_y + s1_x * s2_y;
|
|
346
|
+
if (Math.abs(denom) < 1e-8) {
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
const s = (-s1_y * (p1[0] - q1[0]) + s1_x * (p1[1] - q1[1])) / denom;
|
|
350
|
+
const t = (s2_x * (p1[1] - q1[1]) - s2_y * (p1[0] - q1[0])) / denom;
|
|
351
|
+
if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
|
|
352
|
+
return [p1[0] + t * s1_x, p1[1] + t * s1_y];
|
|
353
|
+
}
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
const viewportsInfo = this._getViewportsInfo();
|
|
357
|
+
if (!viewportsInfo || viewportsInfo.length === 0) {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
let renderStatus = false;
|
|
361
|
+
const { viewport, renderingEngine } = enabledElement;
|
|
362
|
+
const { element } = viewport;
|
|
363
|
+
let annotations = this._getAnnotations(enabledElement);
|
|
364
|
+
if (this._virtualAnnotations && this._virtualAnnotations.length) {
|
|
365
|
+
annotations = annotations.concat(this._virtualAnnotations);
|
|
366
|
+
}
|
|
367
|
+
const camera = viewport.getCamera();
|
|
368
|
+
const filteredToolAnnotations = this.filterInteractableAnnotationsForElement(element, annotations);
|
|
369
|
+
const viewportAnnotation = filteredToolAnnotations[0];
|
|
370
|
+
if (!viewportAnnotation || !viewportAnnotation.data) {
|
|
371
|
+
return renderStatus;
|
|
372
|
+
}
|
|
373
|
+
const annotationUID = viewportAnnotation.annotationUID;
|
|
374
|
+
const { clientWidth, clientHeight } = viewport.canvas;
|
|
375
|
+
const canvasDiagonalLength = Math.sqrt(clientWidth * clientWidth + clientHeight * clientHeight);
|
|
376
|
+
const data = viewportAnnotation.data;
|
|
377
|
+
const otherViewportAnnotations = annotations;
|
|
378
|
+
const volumeCroppingCenterCanvasMin = viewport.worldToCanvas(this.toolCenterMin);
|
|
379
|
+
const volumeCroppingCenterCanvasMax = viewport.worldToCanvas(this.toolCenterMax);
|
|
380
|
+
const referenceLines = [];
|
|
381
|
+
const canvasBox = [0, 0, clientWidth, clientHeight];
|
|
382
|
+
otherViewportAnnotations.forEach((annotation) => {
|
|
383
|
+
const data = annotation.data;
|
|
384
|
+
const isVirtual = 'isVirtual' in annotation &&
|
|
385
|
+
annotation.isVirtual === true;
|
|
386
|
+
data.handles.toolCenter = this.toolCenter;
|
|
387
|
+
let otherViewport, otherCamera, clientWidth, clientHeight, otherCanvasDiagonalLength, otherCanvasCenter, otherViewportCenterWorld;
|
|
388
|
+
if (isVirtual) {
|
|
389
|
+
const realViewports = viewportsInfo.filter((vp) => vp.viewportId !== data.viewportId);
|
|
390
|
+
if (realViewports.length === 2) {
|
|
391
|
+
const vp1 = renderingEngine.getViewport(realViewports[0].viewportId);
|
|
392
|
+
const vp2 = renderingEngine.getViewport(realViewports[1].viewportId);
|
|
393
|
+
const normal1 = vp1.getCamera().viewPlaneNormal;
|
|
394
|
+
const normal2 = vp2.getCamera().viewPlaneNormal;
|
|
395
|
+
const virtualNormal = vec3.create();
|
|
396
|
+
vec3.cross(virtualNormal, normal1, normal2);
|
|
397
|
+
vec3.normalize(virtualNormal, virtualNormal);
|
|
398
|
+
otherCamera = {
|
|
399
|
+
viewPlaneNormal: virtualNormal,
|
|
400
|
+
position: data.handles.toolCenter,
|
|
401
|
+
focalPoint: data.handles.toolCenter,
|
|
402
|
+
viewUp: [0, 1, 0],
|
|
403
|
+
};
|
|
404
|
+
clientWidth = viewport.canvas.clientWidth;
|
|
405
|
+
clientHeight = viewport.canvas.clientHeight;
|
|
406
|
+
otherCanvasDiagonalLength = Math.sqrt(clientWidth * clientWidth + clientHeight * clientHeight);
|
|
407
|
+
otherCanvasCenter = [clientWidth * 0.5, clientHeight * 0.5];
|
|
408
|
+
otherViewportCenterWorld = data.handles.toolCenter;
|
|
409
|
+
otherViewport = {
|
|
410
|
+
id: data.viewportId,
|
|
411
|
+
canvas: viewport.canvas,
|
|
412
|
+
canvasToWorld: () => data.handles.toolCenter,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
const virtualNormal = annotation
|
|
417
|
+
.virtualNormal ?? [0, 0, 1];
|
|
418
|
+
otherCamera = {
|
|
419
|
+
viewPlaneNormal: virtualNormal,
|
|
420
|
+
position: data.handles.toolCenter,
|
|
421
|
+
focalPoint: data.handles.toolCenter,
|
|
422
|
+
viewUp: [0, 1, 0],
|
|
423
|
+
};
|
|
424
|
+
clientWidth = viewport.canvas.clientWidth;
|
|
425
|
+
clientHeight = viewport.canvas.clientHeight;
|
|
426
|
+
otherCanvasDiagonalLength = Math.sqrt(clientWidth * clientWidth + clientHeight * clientHeight);
|
|
427
|
+
otherCanvasCenter = [clientWidth * 0.5, clientHeight * 0.5];
|
|
428
|
+
otherViewportCenterWorld = data.handles.toolCenter;
|
|
429
|
+
otherViewport = {
|
|
430
|
+
id: data.viewportId,
|
|
431
|
+
canvas: viewport.canvas,
|
|
432
|
+
canvasToWorld: () => data.handles.toolCenter,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
otherViewport = renderingEngine.getViewport(data.viewportId);
|
|
438
|
+
otherCamera = otherViewport.getCamera();
|
|
439
|
+
clientWidth = otherViewport.canvas.clientWidth;
|
|
440
|
+
clientHeight = otherViewport.canvas.clientHeight;
|
|
441
|
+
otherCanvasDiagonalLength = Math.sqrt(clientWidth * clientWidth + clientHeight * clientHeight);
|
|
442
|
+
otherCanvasCenter = [clientWidth * 0.5, clientHeight * 0.5];
|
|
443
|
+
otherViewportCenterWorld =
|
|
444
|
+
otherViewport.canvasToWorld(otherCanvasCenter);
|
|
445
|
+
}
|
|
446
|
+
const otherViewportControllable = this._getReferenceLineControllable(otherViewport.id);
|
|
447
|
+
const direction = [0, 0, 0];
|
|
448
|
+
vtkMath.cross(camera.viewPlaneNormal, otherCamera.viewPlaneNormal, direction);
|
|
449
|
+
vtkMath.normalize(direction);
|
|
450
|
+
vtkMath.multiplyScalar(direction, otherCanvasDiagonalLength);
|
|
451
|
+
const pointWorld0 = [0, 0, 0];
|
|
452
|
+
vtkMath.add(otherViewportCenterWorld, direction, pointWorld0);
|
|
453
|
+
const pointWorld1 = [0, 0, 0];
|
|
454
|
+
vtkMath.subtract(otherViewportCenterWorld, direction, pointWorld1);
|
|
455
|
+
const pointCanvas0 = viewport.worldToCanvas(pointWorld0);
|
|
456
|
+
const otherViewportCenterCanvas = viewport.worldToCanvas([
|
|
457
|
+
otherViewportCenterWorld[0] ?? 0,
|
|
458
|
+
otherViewportCenterWorld[1] ?? 0,
|
|
459
|
+
otherViewportCenterWorld[2] ?? 0,
|
|
460
|
+
]);
|
|
461
|
+
const canvasUnitVectorFromCenter = vec2.create();
|
|
462
|
+
vec2.subtract(canvasUnitVectorFromCenter, pointCanvas0, otherViewportCenterCanvas);
|
|
463
|
+
vec2.normalize(canvasUnitVectorFromCenter, canvasUnitVectorFromCenter);
|
|
464
|
+
const canvasVectorFromCenterLong = vec2.create();
|
|
465
|
+
vec2.scale(canvasVectorFromCenterLong, canvasUnitVectorFromCenter, canvasDiagonalLength * 100);
|
|
466
|
+
const refLinesCenterMin = otherViewportControllable
|
|
467
|
+
? vec2.clone(volumeCroppingCenterCanvasMin)
|
|
468
|
+
: vec2.clone(otherViewportCenterCanvas);
|
|
469
|
+
const refLinePointMinOne = vec2.create();
|
|
470
|
+
const refLinePointMinTwo = vec2.create();
|
|
471
|
+
vec2.add(refLinePointMinOne, refLinesCenterMin, canvasVectorFromCenterLong);
|
|
472
|
+
vec2.subtract(refLinePointMinTwo, refLinesCenterMin, canvasVectorFromCenterLong);
|
|
473
|
+
liangBarksyClip(refLinePointMinOne, refLinePointMinTwo, canvasBox);
|
|
474
|
+
referenceLines.push([
|
|
475
|
+
otherViewport,
|
|
476
|
+
refLinePointMinOne,
|
|
477
|
+
refLinePointMinTwo,
|
|
478
|
+
'min',
|
|
479
|
+
]);
|
|
480
|
+
const refLinesCenterMax = otherViewportControllable
|
|
481
|
+
? vec2.clone(volumeCroppingCenterCanvasMax)
|
|
482
|
+
: vec2.clone(otherViewportCenterCanvas);
|
|
483
|
+
const refLinePointMaxOne = vec2.create();
|
|
484
|
+
const refLinePointMaxTwo = vec2.create();
|
|
485
|
+
vec2.add(refLinePointMaxOne, refLinesCenterMax, canvasVectorFromCenterLong);
|
|
486
|
+
vec2.subtract(refLinePointMaxTwo, refLinesCenterMax, canvasVectorFromCenterLong);
|
|
487
|
+
liangBarksyClip(refLinePointMaxOne, refLinePointMaxTwo, canvasBox);
|
|
488
|
+
referenceLines.push([
|
|
489
|
+
otherViewport,
|
|
490
|
+
refLinePointMaxOne,
|
|
491
|
+
refLinePointMaxTwo,
|
|
492
|
+
'max',
|
|
493
|
+
]);
|
|
494
|
+
});
|
|
495
|
+
data.referenceLines = referenceLines;
|
|
496
|
+
const viewportColor = this._getReferenceLineColor(viewport.id);
|
|
497
|
+
const color = viewportColor !== undefined ? viewportColor : 'rgb(200, 200, 200)';
|
|
498
|
+
referenceLines.forEach((line, lineIndex) => {
|
|
499
|
+
const intersections = [];
|
|
500
|
+
for (let j = 0; j < referenceLines.length; ++j) {
|
|
501
|
+
if (j === lineIndex) {
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
const otherLine = referenceLines[j];
|
|
505
|
+
const intersection = lineIntersection2D(line[1], line[2], otherLine[1], otherLine[2]);
|
|
506
|
+
if (intersection) {
|
|
507
|
+
intersections.push({
|
|
508
|
+
with: otherLine[3],
|
|
509
|
+
point: intersection,
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
const otherViewport = line[0];
|
|
514
|
+
let orientation = null;
|
|
515
|
+
if (otherViewport && otherViewport.id) {
|
|
516
|
+
const annotationForViewport = annotations.find((a) => a.data.viewportId === otherViewport.id);
|
|
517
|
+
if (annotationForViewport && annotationForViewport.data.orientation) {
|
|
518
|
+
orientation = String(annotationForViewport.data.orientation).toUpperCase();
|
|
519
|
+
}
|
|
520
|
+
else {
|
|
521
|
+
const idUpper = otherViewport.id.toUpperCase();
|
|
522
|
+
if (idUpper.includes('AXIAL')) {
|
|
523
|
+
orientation = 'AXIAL';
|
|
524
|
+
}
|
|
525
|
+
else if (idUpper.includes('CORONAL')) {
|
|
526
|
+
orientation = 'CORONAL';
|
|
527
|
+
}
|
|
528
|
+
else if (idUpper.includes('SAGITTAL')) {
|
|
529
|
+
orientation = 'SAGITTAL';
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
const lineColors = this.configuration.lineColors || {};
|
|
534
|
+
const colorArr = lineColors[orientation] ||
|
|
535
|
+
lineColors.unknown || [1.0, 0.0, 0.0];
|
|
536
|
+
const color = Array.isArray(colorArr)
|
|
537
|
+
? `rgb(${colorArr.map((v) => Math.round(v * 255)).join(',')})`
|
|
538
|
+
: colorArr;
|
|
539
|
+
const viewportControllable = this._getReferenceLineControllable(otherViewport.id);
|
|
540
|
+
const selectedViewportId = data.activeViewportIds.find((id) => id === otherViewport.id);
|
|
541
|
+
let lineWidth = this.configuration.lineWidth ?? 1.5;
|
|
542
|
+
const lineActive = data.handles.activeOperation !== null &&
|
|
543
|
+
data.handles.activeOperation === OPERATION.DRAG &&
|
|
544
|
+
selectedViewportId;
|
|
545
|
+
if (lineActive) {
|
|
546
|
+
lineWidth = this.configuration.activeLineWidth ?? 2.5;
|
|
547
|
+
}
|
|
548
|
+
const lineUID = `${lineIndex}`;
|
|
549
|
+
if (viewportControllable) {
|
|
550
|
+
if (intersections.length === 2) {
|
|
551
|
+
drawLineSvg(svgDrawingHelper, annotationUID, lineUID, intersections[0].point, intersections[1].point, {
|
|
552
|
+
color,
|
|
553
|
+
lineWidth,
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
if (this.configuration.extendReferenceLines &&
|
|
557
|
+
intersections.length === 2) {
|
|
558
|
+
if (this.configuration.extendReferenceLines &&
|
|
559
|
+
intersections.length === 2) {
|
|
560
|
+
const sortedIntersections = intersections
|
|
561
|
+
.map((intersection) => ({
|
|
562
|
+
...intersection,
|
|
563
|
+
distance: vec2.distance(line[1], intersection.point),
|
|
564
|
+
}))
|
|
565
|
+
.sort((a, b) => a.distance - b.distance);
|
|
566
|
+
drawLineSvg(svgDrawingHelper, annotationUID, lineUID + '_dashed_before', line[1], sortedIntersections[0].point, { color, lineWidth, lineDash: [4, 4] });
|
|
567
|
+
drawLineSvg(svgDrawingHelper, annotationUID, lineUID + '_dashed_after', sortedIntersections[1].point, line[2], { color, lineWidth, lineDash: [4, 4] });
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
renderStatus = true;
|
|
573
|
+
if (this.configuration.viewportIndicators) {
|
|
574
|
+
const { viewportIndicatorsConfig } = this.configuration;
|
|
575
|
+
const xOffset = viewportIndicatorsConfig?.xOffset || 0.95;
|
|
576
|
+
const yOffset = viewportIndicatorsConfig?.yOffset || 0.05;
|
|
577
|
+
const referenceColorCoordinates = [
|
|
578
|
+
clientWidth * xOffset,
|
|
579
|
+
clientHeight * yOffset,
|
|
580
|
+
];
|
|
581
|
+
const circleRadius = viewportIndicatorsConfig?.circleRadius || canvasDiagonalLength * 0.01;
|
|
582
|
+
const circleUID = '0';
|
|
583
|
+
drawCircleSvg(svgDrawingHelper, annotationUID, circleUID, referenceColorCoordinates, circleRadius, { color, fill: color });
|
|
584
|
+
}
|
|
585
|
+
return renderStatus;
|
|
586
|
+
};
|
|
587
|
+
this._getAnnotations = (enabledElement) => {
|
|
588
|
+
const { viewport } = enabledElement;
|
|
589
|
+
const annotations = getAnnotations(this.getToolName(), viewport.element) || [];
|
|
590
|
+
const viewportIds = this._getViewportsInfo().map(({ viewportId }) => viewportId);
|
|
591
|
+
const toolGroupAnnotations = annotations.filter((annotation) => {
|
|
592
|
+
const { data } = annotation;
|
|
593
|
+
return viewportIds.includes(data.viewportId);
|
|
594
|
+
});
|
|
595
|
+
return toolGroupAnnotations;
|
|
596
|
+
};
|
|
597
|
+
this._onSphereMoved = (evt) => {
|
|
598
|
+
if (evt.detail.originalClippingPlanes) {
|
|
599
|
+
this._syncWithVolumeCroppingTool(evt.detail.originalClippingPlanes);
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
602
|
+
if (evt.detail.seriesInstanceUID !== this.seriesInstanceUID) {
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
const { draggingSphereIndex, toolCenter } = evt.detail;
|
|
606
|
+
const newMin = [...this.toolCenterMin];
|
|
607
|
+
const newMax = [...this.toolCenterMax];
|
|
608
|
+
if (draggingSphereIndex >= 0 && draggingSphereIndex <= 5) {
|
|
609
|
+
const axis = Math.floor(draggingSphereIndex / 2);
|
|
610
|
+
const isMin = draggingSphereIndex % 2 === 0;
|
|
611
|
+
(isMin ? newMin : newMax)[axis] = toolCenter[axis];
|
|
612
|
+
this.setToolCenter(newMin, 'min');
|
|
613
|
+
this.setToolCenter(newMax, 'max');
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
if (draggingSphereIndex >= 6 && draggingSphereIndex <= 13) {
|
|
617
|
+
const idx = draggingSphereIndex;
|
|
618
|
+
if (idx < 10) {
|
|
619
|
+
newMin[0] = toolCenter[0];
|
|
620
|
+
}
|
|
621
|
+
else {
|
|
622
|
+
newMax[0] = toolCenter[0];
|
|
623
|
+
}
|
|
624
|
+
if ([6, 7, 10, 11].includes(idx)) {
|
|
625
|
+
newMin[1] = toolCenter[1];
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
newMax[1] = toolCenter[1];
|
|
629
|
+
}
|
|
630
|
+
if (idx % 2 === 0) {
|
|
631
|
+
newMin[2] = toolCenter[2];
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
newMax[2] = toolCenter[2];
|
|
635
|
+
}
|
|
636
|
+
this.setToolCenter(newMin, 'min');
|
|
637
|
+
this.setToolCenter(newMax, 'max');
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
this._onNewVolume = () => {
|
|
642
|
+
const viewportsInfo = this._getViewportsInfo();
|
|
643
|
+
if (viewportsInfo && viewportsInfo.length > 0) {
|
|
644
|
+
const { viewportId, renderingEngineId } = viewportsInfo[0];
|
|
645
|
+
const renderingEngine = getRenderingEngine(renderingEngineId);
|
|
646
|
+
const viewport = renderingEngine.getViewport(viewportId);
|
|
647
|
+
const volumeActors = viewport.getActors();
|
|
648
|
+
if (volumeActors.length > 0) {
|
|
649
|
+
const imageData = volumeActors[0].actor.getMapper().getInputData();
|
|
650
|
+
if (imageData) {
|
|
651
|
+
this.seriesInstanceUID = imageData.seriesInstanceUID;
|
|
652
|
+
this._updateToolCentersFromViewport(viewport);
|
|
653
|
+
const annotations = getAnnotations(this.getToolName(), viewportId) || [];
|
|
654
|
+
annotations.forEach((annotation) => {
|
|
655
|
+
if (annotation.data && annotation.data.handles) {
|
|
656
|
+
annotation.data.handles.toolCenter = [...this.toolCenter];
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
this._computeToolCenter(viewportsInfo);
|
|
663
|
+
triggerEvent(eventTarget, Events.VOLUMECROPPINGCONTROL_TOOL_CHANGED, {
|
|
664
|
+
toolGroupId: this.toolGroupId,
|
|
665
|
+
viewportsInfo: viewportsInfo,
|
|
666
|
+
seriesInstanceUID: this.seriesInstanceUID,
|
|
667
|
+
});
|
|
668
|
+
};
|
|
669
|
+
this._getAnnotationsForViewportsWithDifferentCameras = (enabledElement, annotations) => {
|
|
670
|
+
const { viewportId, renderingEngine, viewport } = enabledElement;
|
|
671
|
+
const otherViewportAnnotations = annotations.filter((annotation) => annotation.data.viewportId !== viewportId);
|
|
672
|
+
if (!otherViewportAnnotations || !otherViewportAnnotations.length) {
|
|
673
|
+
return [];
|
|
674
|
+
}
|
|
675
|
+
const camera = viewport.getCamera();
|
|
676
|
+
const { viewPlaneNormal, position } = camera;
|
|
677
|
+
const viewportsWithDifferentCameras = otherViewportAnnotations.filter((annotation) => {
|
|
678
|
+
const { viewportId } = annotation.data;
|
|
679
|
+
const targetViewport = renderingEngine.getViewport(viewportId);
|
|
680
|
+
const cameraOfTarget = targetViewport.getCamera();
|
|
681
|
+
return !(csUtils.isEqual(cameraOfTarget.viewPlaneNormal, viewPlaneNormal, 1e-2) && csUtils.isEqual(cameraOfTarget.position, position, 1));
|
|
682
|
+
});
|
|
683
|
+
return viewportsWithDifferentCameras;
|
|
684
|
+
};
|
|
685
|
+
this._filterViewportWithSameOrientation = (enabledElement, referenceAnnotation, annotations) => {
|
|
686
|
+
const { renderingEngine } = enabledElement;
|
|
687
|
+
const { data } = referenceAnnotation;
|
|
688
|
+
const viewport = renderingEngine.getViewport(data.viewportId);
|
|
689
|
+
const linkedViewportAnnotations = annotations.filter((annotation) => {
|
|
690
|
+
const { data } = annotation;
|
|
691
|
+
const otherViewport = renderingEngine.getViewport(data.viewportId);
|
|
692
|
+
const otherViewportControllable = this._getReferenceLineControllable(otherViewport.id);
|
|
693
|
+
return otherViewportControllable === true;
|
|
694
|
+
});
|
|
695
|
+
if (!linkedViewportAnnotations || !linkedViewportAnnotations.length) {
|
|
696
|
+
return [];
|
|
697
|
+
}
|
|
698
|
+
const camera = viewport.getCamera();
|
|
699
|
+
const viewPlaneNormal = camera.viewPlaneNormal;
|
|
700
|
+
vtkMath.normalize(viewPlaneNormal);
|
|
701
|
+
const otherViewportsAnnotationsWithSameCameraDirection = linkedViewportAnnotations.filter((annotation) => {
|
|
702
|
+
const { viewportId } = annotation.data;
|
|
703
|
+
const otherViewport = renderingEngine.getViewport(viewportId);
|
|
704
|
+
const otherCamera = otherViewport.getCamera();
|
|
705
|
+
const otherViewPlaneNormal = otherCamera.viewPlaneNormal;
|
|
706
|
+
vtkMath.normalize(otherViewPlaneNormal);
|
|
707
|
+
return (csUtils.isEqual(viewPlaneNormal, otherViewPlaneNormal, 1e-2) &&
|
|
708
|
+
csUtils.isEqual(camera.viewUp, otherCamera.viewUp, 1e-2));
|
|
709
|
+
});
|
|
710
|
+
return otherViewportsAnnotationsWithSameCameraDirection;
|
|
711
|
+
};
|
|
712
|
+
this._activateModify = (element) => {
|
|
713
|
+
state.isInteractingWithTool = !this.configuration.mobile?.enabled;
|
|
714
|
+
element.addEventListener(Events.MOUSE_UP, this._endCallback);
|
|
715
|
+
element.addEventListener(Events.MOUSE_DRAG, this._dragCallback);
|
|
716
|
+
element.addEventListener(Events.MOUSE_CLICK, this._endCallback);
|
|
717
|
+
element.addEventListener(Events.TOUCH_END, this._endCallback);
|
|
718
|
+
element.addEventListener(Events.TOUCH_DRAG, this._dragCallback);
|
|
719
|
+
element.addEventListener(Events.TOUCH_TAP, this._endCallback);
|
|
720
|
+
};
|
|
721
|
+
this._deactivateModify = (element) => {
|
|
722
|
+
state.isInteractingWithTool = false;
|
|
723
|
+
element.removeEventListener(Events.MOUSE_UP, this._endCallback);
|
|
724
|
+
element.removeEventListener(Events.MOUSE_DRAG, this._dragCallback);
|
|
725
|
+
element.removeEventListener(Events.MOUSE_CLICK, this._endCallback);
|
|
726
|
+
element.removeEventListener(Events.TOUCH_END, this._endCallback);
|
|
727
|
+
element.removeEventListener(Events.TOUCH_DRAG, this._dragCallback);
|
|
728
|
+
element.removeEventListener(Events.TOUCH_TAP, this._endCallback);
|
|
729
|
+
};
|
|
730
|
+
this._endCallback = (evt) => {
|
|
731
|
+
const eventDetail = evt.detail;
|
|
732
|
+
const { element } = eventDetail;
|
|
733
|
+
this.editData.annotation.data.handles.activeOperation = null;
|
|
734
|
+
this.editData.annotation.data.activeViewportIds = [];
|
|
735
|
+
this._deactivateModify(element);
|
|
736
|
+
resetElementCursor(element);
|
|
737
|
+
this.editData = null;
|
|
738
|
+
const requireSameOrientation = false;
|
|
739
|
+
const viewportIdsToRender = getViewportIdsWithToolToRender(element, this.getToolName(), requireSameOrientation);
|
|
740
|
+
triggerAnnotationRenderForViewportIds(viewportIdsToRender);
|
|
741
|
+
};
|
|
742
|
+
this._dragCallback = (evt) => {
|
|
743
|
+
const eventDetail = evt.detail;
|
|
744
|
+
const delta = eventDetail.deltaPoints.world;
|
|
745
|
+
if (Math.abs(delta[0]) < 1e-3 &&
|
|
746
|
+
Math.abs(delta[1]) < 1e-3 &&
|
|
747
|
+
Math.abs(delta[2]) < 1e-3) {
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
const { element } = eventDetail;
|
|
751
|
+
const enabledElement = getEnabledElement(element);
|
|
752
|
+
const { viewport } = enabledElement;
|
|
753
|
+
if (viewport.type === Enums.ViewportType.VOLUME_3D) {
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
const annotations = this._getAnnotations(enabledElement);
|
|
757
|
+
const filteredToolAnnotations = this.filterInteractableAnnotationsForElement(element, annotations);
|
|
758
|
+
const viewportAnnotation = filteredToolAnnotations[0];
|
|
759
|
+
if (!viewportAnnotation) {
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
const { handles } = viewportAnnotation.data;
|
|
763
|
+
if (handles.activeOperation === OPERATION.DRAG) {
|
|
764
|
+
if (handles.activeType === 'min') {
|
|
765
|
+
this.toolCenterMin[0] += delta[0];
|
|
766
|
+
this.toolCenterMin[1] += delta[1];
|
|
767
|
+
this.toolCenterMin[2] += delta[2];
|
|
768
|
+
}
|
|
769
|
+
else if (handles.activeType === 'max') {
|
|
770
|
+
this.toolCenterMax[0] += delta[0];
|
|
771
|
+
this.toolCenterMax[1] += delta[1];
|
|
772
|
+
this.toolCenterMax[2] += delta[2];
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
this.toolCenter[0] += delta[0];
|
|
776
|
+
this.toolCenter[1] += delta[1];
|
|
777
|
+
this.toolCenter[2] += delta[2];
|
|
778
|
+
}
|
|
779
|
+
const viewportsInfo = this._getViewportsInfo();
|
|
780
|
+
triggerAnnotationRenderForViewportIds(viewportsInfo.map(({ viewportId }) => viewportId));
|
|
781
|
+
triggerEvent(eventTarget, Events.VOLUMECROPPINGCONTROL_TOOL_CHANGED, {
|
|
782
|
+
toolGroupId: this.toolGroupId,
|
|
783
|
+
toolCenter: this.toolCenter,
|
|
784
|
+
toolCenterMin: this.toolCenterMin,
|
|
785
|
+
toolCenterMax: this.toolCenterMax,
|
|
786
|
+
handleType: handles.activeType,
|
|
787
|
+
viewportOrientation: [],
|
|
788
|
+
seriesInstanceUID: this.seriesInstanceUID,
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
this._getReferenceLineColor =
|
|
793
|
+
toolProps.configuration?.getReferenceLineColor ||
|
|
794
|
+
defaultReferenceLineColor;
|
|
795
|
+
this._getReferenceLineControllable =
|
|
796
|
+
toolProps.configuration?.getReferenceLineControllable ||
|
|
797
|
+
defaultReferenceLineControllable;
|
|
798
|
+
const viewportsInfo = getToolGroup(this.toolGroupId)?.viewportsInfo;
|
|
799
|
+
eventTarget.addEventListener(Events.VOLUMECROPPING_TOOL_CHANGED, this._onSphereMoved);
|
|
800
|
+
if (viewportsInfo && viewportsInfo.length > 0) {
|
|
801
|
+
const { viewportId, renderingEngineId } = viewportsInfo[0];
|
|
802
|
+
const enabledElement = getEnabledElementByIds(viewportId, renderingEngineId);
|
|
803
|
+
const renderingEngine = getRenderingEngine(renderingEngineId);
|
|
804
|
+
const viewport = renderingEngine.getViewport(viewportId);
|
|
805
|
+
const volumeActors = viewport.getActors();
|
|
806
|
+
if (!volumeActors || !volumeActors.length) {
|
|
807
|
+
console.warn(`VolumeCroppingControlTool: No volume actors found in viewport ${viewportId}.`);
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
const imageData = volumeActors[0].actor.getMapper().getInputData();
|
|
811
|
+
if (imageData) {
|
|
812
|
+
const dimensions = imageData.getDimensions();
|
|
813
|
+
const spacing = imageData.getSpacing();
|
|
814
|
+
const origin = imageData.getOrigin();
|
|
815
|
+
this.seriesInstanceUID = imageData.seriesInstanceUID || 'unknown';
|
|
816
|
+
const cropFactor = this.configuration.initialCropFactor ?? 0.2;
|
|
817
|
+
this.toolCenter = [
|
|
818
|
+
origin[0] + cropFactor * (dimensions[0] - 1) * spacing[0],
|
|
819
|
+
origin[1] + cropFactor * (dimensions[1] - 1) * spacing[1],
|
|
820
|
+
origin[2] + cropFactor * (dimensions[2] - 1) * spacing[2],
|
|
821
|
+
];
|
|
822
|
+
const maxCropFactor = 1 - cropFactor;
|
|
823
|
+
this.toolCenterMin = [
|
|
824
|
+
origin[0] + cropFactor * (dimensions[0] - 1) * spacing[0],
|
|
825
|
+
origin[1] + cropFactor * (dimensions[1] - 1) * spacing[1],
|
|
826
|
+
origin[2] + cropFactor * (dimensions[2] - 1) * spacing[2],
|
|
827
|
+
];
|
|
828
|
+
this.toolCenterMax = [
|
|
829
|
+
origin[0] + maxCropFactor * (dimensions[0] - 1) * spacing[0],
|
|
830
|
+
origin[1] + maxCropFactor * (dimensions[1] - 1) * spacing[1],
|
|
831
|
+
origin[2] + maxCropFactor * (dimensions[2] - 1) * spacing[2],
|
|
832
|
+
];
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
_updateToolCentersFromViewport(viewport) {
|
|
837
|
+
const volumeActors = viewport.getActors();
|
|
838
|
+
if (!volumeActors || !volumeActors.length) {
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
const imageData = volumeActors[0].actor.getMapper().getInputData();
|
|
842
|
+
if (!imageData) {
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
this.seriesInstanceUID = imageData.seriesInstanceUID || 'unknown';
|
|
846
|
+
const dimensions = imageData.getDimensions();
|
|
847
|
+
const spacing = imageData.getSpacing();
|
|
848
|
+
const origin = imageData.getOrigin();
|
|
849
|
+
const cropFactor = this.configuration.initialCropFactor ?? 0.2;
|
|
850
|
+
const cropStart = cropFactor / 2;
|
|
851
|
+
const cropEnd = 1 - cropFactor / 2;
|
|
852
|
+
this.toolCenter = [
|
|
853
|
+
origin[0] +
|
|
854
|
+
((cropStart + cropEnd) / 2) * (dimensions[0] - 1) * spacing[0],
|
|
855
|
+
origin[1] +
|
|
856
|
+
((cropStart + cropEnd) / 2) * (dimensions[1] - 1) * spacing[1],
|
|
857
|
+
origin[2] +
|
|
858
|
+
((cropStart + cropEnd) / 2) * (dimensions[2] - 1) * spacing[2],
|
|
859
|
+
];
|
|
860
|
+
this.toolCenterMin = [
|
|
861
|
+
origin[0] + cropStart * (dimensions[0] - 1) * spacing[0],
|
|
862
|
+
origin[1] + cropStart * (dimensions[1] - 1) * spacing[1],
|
|
863
|
+
origin[2] + cropStart * (dimensions[2] - 1) * spacing[2],
|
|
864
|
+
];
|
|
865
|
+
this.toolCenterMax = [
|
|
866
|
+
origin[0] + cropEnd * (dimensions[0] - 1) * spacing[0],
|
|
867
|
+
origin[1] + cropEnd * (dimensions[1] - 1) * spacing[1],
|
|
868
|
+
origin[2] + cropEnd * (dimensions[2] - 1) * spacing[2],
|
|
869
|
+
];
|
|
870
|
+
}
|
|
871
|
+
onSetToolInactive() {
|
|
872
|
+
console.debug(`VolumeCroppingControlTool: onSetToolInactive called for tool ${this.getToolName()}`);
|
|
873
|
+
}
|
|
874
|
+
onSetToolActive() {
|
|
875
|
+
const viewportsInfo = this._getViewportsInfo();
|
|
876
|
+
let anyAnnotationExists = false;
|
|
877
|
+
for (const vpInfo of viewportsInfo) {
|
|
878
|
+
const enabledElement = getEnabledElementByIds(vpInfo.viewportId, vpInfo.renderingEngineId);
|
|
879
|
+
const annotations = this._getAnnotations(enabledElement);
|
|
880
|
+
if (annotations && annotations.length > 0) {
|
|
881
|
+
anyAnnotationExists = true;
|
|
882
|
+
break;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
if (!anyAnnotationExists) {
|
|
886
|
+
this._unsubscribeToViewportNewVolumeSet(viewportsInfo);
|
|
887
|
+
this._subscribeToViewportNewVolumeSet(viewportsInfo);
|
|
888
|
+
this._computeToolCenter(viewportsInfo);
|
|
889
|
+
triggerEvent(eventTarget, Events.VOLUMECROPPINGCONTROL_TOOL_CHANGED, {
|
|
890
|
+
toolGroupId: this.toolGroupId,
|
|
891
|
+
viewportsInfo: viewportsInfo,
|
|
892
|
+
seriesInstanceUID: this.seriesInstanceUID,
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
else {
|
|
896
|
+
for (const vpInfo of viewportsInfo) {
|
|
897
|
+
const enabledElement = getEnabledElementByIds(vpInfo.viewportId, vpInfo.renderingEngineId);
|
|
898
|
+
if (!enabledElement) {
|
|
899
|
+
continue;
|
|
900
|
+
}
|
|
901
|
+
const annotations = this._getAnnotations(enabledElement);
|
|
902
|
+
if (annotations && annotations.length > 0) {
|
|
903
|
+
annotations.forEach((annotation) => {
|
|
904
|
+
removeAnnotation(annotation.annotationUID);
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
enabledElement.viewport.render();
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
onSetToolEnabled() {
|
|
912
|
+
console.debug(`VolumeCroppingControlTool: onSetToolEnabled called for tool ${this.getToolName()}`);
|
|
913
|
+
const viewportsInfo = this._getViewportsInfo();
|
|
914
|
+
}
|
|
915
|
+
onSetToolDisabled() {
|
|
916
|
+
console.debug(`VolumeCroppingControlTool: onSetToolDisabled called for tool ${this.getToolName()}`);
|
|
917
|
+
const viewportsInfo = this._getViewportsInfo();
|
|
918
|
+
this._unsubscribeToViewportNewVolumeSet(viewportsInfo);
|
|
919
|
+
viewportsInfo.forEach(({ renderingEngineId, viewportId }) => {
|
|
920
|
+
const enabledElement = getEnabledElementByIds(viewportId, renderingEngineId);
|
|
921
|
+
if (!enabledElement) {
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
const annotations = this._getAnnotations(enabledElement);
|
|
925
|
+
if (annotations?.length) {
|
|
926
|
+
annotations.forEach((annotation) => {
|
|
927
|
+
removeAnnotation(annotation.annotationUID);
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
_getOrientationFromNormal(normal) {
|
|
933
|
+
if (!normal) {
|
|
934
|
+
return null;
|
|
935
|
+
}
|
|
936
|
+
const canonical = {
|
|
937
|
+
AXIAL: [0, 0, 1],
|
|
938
|
+
CORONAL: [0, 1, 0],
|
|
939
|
+
SAGITTAL: [1, 0, 0],
|
|
940
|
+
};
|
|
941
|
+
const tol = 1e-2;
|
|
942
|
+
for (const [key, value] of Object.entries(canonical)) {
|
|
943
|
+
if (Math.abs(normal[0] - value[0]) < tol &&
|
|
944
|
+
Math.abs(normal[1] - value[1]) < tol &&
|
|
945
|
+
Math.abs(normal[2] - value[2]) < tol) {
|
|
946
|
+
return key;
|
|
947
|
+
}
|
|
948
|
+
if (Math.abs(normal[0] + value[0]) < tol &&
|
|
949
|
+
Math.abs(normal[1] + value[1]) < tol &&
|
|
950
|
+
Math.abs(normal[2] + value[2]) < tol) {
|
|
951
|
+
return key;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
return null;
|
|
955
|
+
}
|
|
956
|
+
_syncWithVolumeCroppingTool(originalClippingPlanes) {
|
|
957
|
+
const planes = originalClippingPlanes;
|
|
958
|
+
if (planes.length >= 6) {
|
|
959
|
+
this.toolCenterMin = [
|
|
960
|
+
planes[0].origin[0],
|
|
961
|
+
planes[2].origin[1],
|
|
962
|
+
planes[4].origin[2],
|
|
963
|
+
];
|
|
964
|
+
this.toolCenterMax = [
|
|
965
|
+
planes[1].origin[0],
|
|
966
|
+
planes[3].origin[1],
|
|
967
|
+
planes[5].origin[2],
|
|
968
|
+
];
|
|
969
|
+
this.toolCenter = [
|
|
970
|
+
(this.toolCenterMin[0] + this.toolCenterMax[0]) / 2,
|
|
971
|
+
(this.toolCenterMin[1] + this.toolCenterMax[1]) / 2,
|
|
972
|
+
(this.toolCenterMin[2] + this.toolCenterMax[2]) / 2,
|
|
973
|
+
];
|
|
974
|
+
const viewportsInfo = this._getViewportsInfo();
|
|
975
|
+
viewportsInfo.forEach(({ viewportId, renderingEngineId }) => {
|
|
976
|
+
const enabledElement = getEnabledElementByIds(viewportId, renderingEngineId);
|
|
977
|
+
if (enabledElement) {
|
|
978
|
+
const annotations = this._getAnnotations(enabledElement);
|
|
979
|
+
annotations.forEach((annotation) => {
|
|
980
|
+
if (annotation.data &&
|
|
981
|
+
annotation.data.handles &&
|
|
982
|
+
annotation.data.orientation) {
|
|
983
|
+
const orientation = annotation.data.orientation;
|
|
984
|
+
if (orientation === 'AXIAL') {
|
|
985
|
+
annotation.data.handles.toolCenterMin = [
|
|
986
|
+
planes[0].origin[0],
|
|
987
|
+
planes[2].origin[1],
|
|
988
|
+
annotation.data.handles.toolCenterMin[2],
|
|
989
|
+
];
|
|
990
|
+
annotation.data.handles.toolCenterMax = [
|
|
991
|
+
planes[1].origin[0],
|
|
992
|
+
planes[3].origin[1],
|
|
993
|
+
annotation.data.handles.toolCenterMax[2],
|
|
994
|
+
];
|
|
995
|
+
}
|
|
996
|
+
else if (orientation === 'CORONAL') {
|
|
997
|
+
annotation.data.handles.toolCenterMin = [
|
|
998
|
+
planes[0].origin[0],
|
|
999
|
+
annotation.data.handles.toolCenterMin[1],
|
|
1000
|
+
planes[4].origin[2],
|
|
1001
|
+
];
|
|
1002
|
+
annotation.data.handles.toolCenterMax = [
|
|
1003
|
+
planes[1].origin[0],
|
|
1004
|
+
annotation.data.handles.toolCenterMax[1],
|
|
1005
|
+
planes[5].origin[2],
|
|
1006
|
+
];
|
|
1007
|
+
}
|
|
1008
|
+
else if (orientation === 'SAGITTAL') {
|
|
1009
|
+
annotation.data.handles.toolCenterMin = [
|
|
1010
|
+
annotation.data.handles.toolCenterMin[0],
|
|
1011
|
+
planes[2].origin[1],
|
|
1012
|
+
planes[4].origin[2],
|
|
1013
|
+
];
|
|
1014
|
+
annotation.data.handles.toolCenterMax = [
|
|
1015
|
+
annotation.data.handles.toolCenterMax[0],
|
|
1016
|
+
planes[3].origin[1],
|
|
1017
|
+
planes[5].origin[2],
|
|
1018
|
+
];
|
|
1019
|
+
}
|
|
1020
|
+
annotation.data.handles.toolCenter = [
|
|
1021
|
+
(annotation.data.handles.toolCenterMin[0] +
|
|
1022
|
+
annotation.data.handles.toolCenterMax[0]) /
|
|
1023
|
+
2,
|
|
1024
|
+
(annotation.data.handles.toolCenterMin[1] +
|
|
1025
|
+
annotation.data.handles.toolCenterMax[1]) /
|
|
1026
|
+
2,
|
|
1027
|
+
(annotation.data.handles.toolCenterMin[2] +
|
|
1028
|
+
annotation.data.handles.toolCenterMax[2]) /
|
|
1029
|
+
2,
|
|
1030
|
+
];
|
|
1031
|
+
}
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
});
|
|
1035
|
+
if (this._virtualAnnotations && this._virtualAnnotations.length > 0) {
|
|
1036
|
+
this._virtualAnnotations.forEach((annotation) => {
|
|
1037
|
+
if (annotation.data &&
|
|
1038
|
+
annotation.data.handles &&
|
|
1039
|
+
annotation.data.orientation) {
|
|
1040
|
+
const orientation = annotation.data.orientation.toUpperCase();
|
|
1041
|
+
if (orientation === 'AXIAL') {
|
|
1042
|
+
annotation.data.handles.toolCenterMin = [
|
|
1043
|
+
planes[0].origin[0],
|
|
1044
|
+
planes[2].origin[1],
|
|
1045
|
+
annotation.data.handles.toolCenterMin[2],
|
|
1046
|
+
];
|
|
1047
|
+
annotation.data.handles.toolCenterMax = [
|
|
1048
|
+
planes[1].origin[0],
|
|
1049
|
+
planes[3].origin[1],
|
|
1050
|
+
annotation.data.handles.toolCenterMax[2],
|
|
1051
|
+
];
|
|
1052
|
+
}
|
|
1053
|
+
else if (orientation === 'CORONAL') {
|
|
1054
|
+
annotation.data.handles.toolCenterMin = [
|
|
1055
|
+
planes[0].origin[0],
|
|
1056
|
+
annotation.data.handles.toolCenterMin[1],
|
|
1057
|
+
planes[4].origin[2],
|
|
1058
|
+
];
|
|
1059
|
+
annotation.data.handles.toolCenterMax = [
|
|
1060
|
+
planes[1].origin[0],
|
|
1061
|
+
annotation.data.handles.toolCenterMax[1],
|
|
1062
|
+
planes[5].origin[2],
|
|
1063
|
+
];
|
|
1064
|
+
}
|
|
1065
|
+
else if (orientation === 'SAGITTAL') {
|
|
1066
|
+
annotation.data.handles.toolCenterMin = [
|
|
1067
|
+
annotation.data.handles.toolCenterMin[0],
|
|
1068
|
+
planes[2].origin[1],
|
|
1069
|
+
planes[4].origin[2],
|
|
1070
|
+
];
|
|
1071
|
+
annotation.data.handles.toolCenterMax = [
|
|
1072
|
+
annotation.data.handles.toolCenterMax[0],
|
|
1073
|
+
planes[3].origin[1],
|
|
1074
|
+
planes[5].origin[2],
|
|
1075
|
+
];
|
|
1076
|
+
}
|
|
1077
|
+
annotation.data.handles.toolCenter = [
|
|
1078
|
+
(annotation.data.handles.toolCenterMin[0] +
|
|
1079
|
+
annotation.data.handles.toolCenterMax[0]) /
|
|
1080
|
+
2,
|
|
1081
|
+
(annotation.data.handles.toolCenterMin[1] +
|
|
1082
|
+
annotation.data.handles.toolCenterMax[1]) /
|
|
1083
|
+
2,
|
|
1084
|
+
(annotation.data.handles.toolCenterMin[2] +
|
|
1085
|
+
annotation.data.handles.toolCenterMax[2]) /
|
|
1086
|
+
2,
|
|
1087
|
+
];
|
|
1088
|
+
}
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
triggerAnnotationRenderForViewportIds(viewportsInfo.map(({ viewportId }) => viewportId));
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
setToolCenter(toolCenter, handleType) {
|
|
1095
|
+
if (handleType === 'min') {
|
|
1096
|
+
this.toolCenterMin = [...toolCenter];
|
|
1097
|
+
}
|
|
1098
|
+
else if (handleType === 'max') {
|
|
1099
|
+
this.toolCenterMax = [...toolCenter];
|
|
1100
|
+
}
|
|
1101
|
+
const viewportsInfo = this._getViewportsInfo();
|
|
1102
|
+
triggerAnnotationRenderForViewportIds(viewportsInfo.map(({ viewportId }) => viewportId));
|
|
1103
|
+
}
|
|
1104
|
+
addNewAnnotation(evt) {
|
|
1105
|
+
const eventDetail = evt.detail;
|
|
1106
|
+
const { element } = eventDetail;
|
|
1107
|
+
const enabledElement = getEnabledElement(element);
|
|
1108
|
+
const { viewport } = enabledElement;
|
|
1109
|
+
const annotations = this._getAnnotations(enabledElement);
|
|
1110
|
+
const filteredAnnotations = this.filterInteractableAnnotationsForElement(viewport.element, annotations);
|
|
1111
|
+
if (!filteredAnnotations ||
|
|
1112
|
+
filteredAnnotations.length === 0 ||
|
|
1113
|
+
!filteredAnnotations[0]) {
|
|
1114
|
+
return null;
|
|
1115
|
+
}
|
|
1116
|
+
const { data } = filteredAnnotations[0];
|
|
1117
|
+
const viewportIdArray = [];
|
|
1118
|
+
const referenceLines = data.referenceLines || [];
|
|
1119
|
+
for (let i = 0; i < referenceLines.length; ++i) {
|
|
1120
|
+
const otherViewport = referenceLines[i][0];
|
|
1121
|
+
const viewportControllable = this._getReferenceLineControllable(otherViewport.id);
|
|
1122
|
+
if (!viewportControllable) {
|
|
1123
|
+
continue;
|
|
1124
|
+
}
|
|
1125
|
+
viewportIdArray.push(otherViewport.id);
|
|
1126
|
+
i++;
|
|
1127
|
+
}
|
|
1128
|
+
data.activeViewportIds = [...viewportIdArray];
|
|
1129
|
+
data.handles.activeOperation = OPERATION.DRAG;
|
|
1130
|
+
evt.preventDefault();
|
|
1131
|
+
hideElementCursor(element);
|
|
1132
|
+
this._activateModify(element);
|
|
1133
|
+
return filteredAnnotations[0];
|
|
1134
|
+
}
|
|
1135
|
+
handleSelectedCallback(evt, annotation, handle, interactionType) {
|
|
1136
|
+
this.toolSelectedCallback(evt, annotation, interactionType);
|
|
1137
|
+
}
|
|
1138
|
+
_unsubscribeToViewportNewVolumeSet(viewportsInfo) {
|
|
1139
|
+
viewportsInfo.forEach(({ viewportId, renderingEngineId }) => {
|
|
1140
|
+
const { viewport } = getEnabledElementByIds(viewportId, renderingEngineId);
|
|
1141
|
+
const { element } = viewport;
|
|
1142
|
+
element.removeEventListener(Enums.Events.VOLUME_VIEWPORT_NEW_VOLUME, this._onNewVolume);
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
_subscribeToViewportNewVolumeSet(viewports) {
|
|
1146
|
+
viewports.forEach(({ viewportId, renderingEngineId }) => {
|
|
1147
|
+
const { viewport } = getEnabledElementByIds(viewportId, renderingEngineId);
|
|
1148
|
+
const { element } = viewport;
|
|
1149
|
+
element.addEventListener(Enums.Events.VOLUME_VIEWPORT_NEW_VOLUME, this._onNewVolume);
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
_applyDeltaShiftToSelectedViewportCameras(renderingEngine, viewportsAnnotationsToUpdate, delta) {
|
|
1153
|
+
viewportsAnnotationsToUpdate.forEach((annotation) => {
|
|
1154
|
+
this._applyDeltaShiftToViewportCamera(renderingEngine, annotation, delta);
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
_applyDeltaShiftToViewportCamera(renderingEngine, annotation, delta) {
|
|
1158
|
+
const { data } = annotation;
|
|
1159
|
+
const viewport = renderingEngine.getViewport(data.viewportId);
|
|
1160
|
+
const camera = viewport.getCamera();
|
|
1161
|
+
const normal = camera.viewPlaneNormal;
|
|
1162
|
+
const dotProd = vtkMath.dot(delta, normal);
|
|
1163
|
+
const projectedDelta = [...normal];
|
|
1164
|
+
vtkMath.multiplyScalar(projectedDelta, dotProd);
|
|
1165
|
+
if (Math.abs(projectedDelta[0]) > 1e-3 ||
|
|
1166
|
+
Math.abs(projectedDelta[1]) > 1e-3 ||
|
|
1167
|
+
Math.abs(projectedDelta[2]) > 1e-3) {
|
|
1168
|
+
const newFocalPoint = [0, 0, 0];
|
|
1169
|
+
const newPosition = [0, 0, 0];
|
|
1170
|
+
vtkMath.add(camera.focalPoint, projectedDelta, newFocalPoint);
|
|
1171
|
+
vtkMath.add(camera.position, projectedDelta, newPosition);
|
|
1172
|
+
viewport.setCamera({
|
|
1173
|
+
focalPoint: newFocalPoint,
|
|
1174
|
+
position: newPosition,
|
|
1175
|
+
});
|
|
1176
|
+
viewport.render();
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
_pointNearTool(element, annotation, canvasCoords, proximity) {
|
|
1180
|
+
const { data } = annotation;
|
|
1181
|
+
const referenceLines = data.referenceLines;
|
|
1182
|
+
const viewportIdArray = [];
|
|
1183
|
+
if (referenceLines) {
|
|
1184
|
+
for (let i = 0; i < referenceLines.length; ++i) {
|
|
1185
|
+
const otherViewport = referenceLines[i][0];
|
|
1186
|
+
const start1 = referenceLines[i][1];
|
|
1187
|
+
const end1 = referenceLines[i][2];
|
|
1188
|
+
const type = referenceLines[i][3];
|
|
1189
|
+
const distance1 = lineSegment.distanceToPoint(start1, end1, [
|
|
1190
|
+
canvasCoords[0],
|
|
1191
|
+
canvasCoords[1],
|
|
1192
|
+
]);
|
|
1193
|
+
if (distance1 <= proximity) {
|
|
1194
|
+
viewportIdArray.push(otherViewport.id);
|
|
1195
|
+
data.handles.activeOperation = 1;
|
|
1196
|
+
data.handles.activeType = type;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
data.activeViewportIds = [...viewportIdArray];
|
|
1201
|
+
this.editData = {
|
|
1202
|
+
annotation,
|
|
1203
|
+
};
|
|
1204
|
+
return data.handles.activeOperation === 1 ? true : false;
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
VolumeCroppingControlTool.toolName = 'VolumeCroppingControl';
|
|
1208
|
+
export default VolumeCroppingControlTool;
|