@cornerstonejs/tools 4.18.2 → 4.18.4

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.
Files changed (35) hide show
  1. package/dist/esm/tools/VolumeCroppingControlTool.d.ts +10 -35
  2. package/dist/esm/tools/VolumeCroppingControlTool.js +179 -699
  3. package/dist/esm/tools/VolumeCroppingTool.d.ts +32 -32
  4. package/dist/esm/tools/VolumeCroppingTool.js +775 -525
  5. package/dist/esm/utilities/draw3D/addLine3DBetweenPoints.d.ts +7 -0
  6. package/dist/esm/utilities/draw3D/addLine3DBetweenPoints.js +34 -0
  7. package/dist/esm/utilities/draw3D/calculateAdaptiveSphereRadius.d.ts +6 -0
  8. package/dist/esm/utilities/draw3D/calculateAdaptiveSphereRadius.js +7 -0
  9. package/dist/esm/utilities/draw3D/index.d.ts +2 -0
  10. package/dist/esm/utilities/draw3D/index.js +2 -0
  11. package/dist/esm/utilities/index.d.ts +2 -1
  12. package/dist/esm/utilities/index.js +2 -1
  13. package/dist/esm/utilities/volumeCropping/computePlanePlaneIntersection.d.ts +6 -0
  14. package/dist/esm/utilities/volumeCropping/computePlanePlaneIntersection.js +37 -0
  15. package/dist/esm/utilities/volumeCropping/constants.d.ts +31 -0
  16. package/dist/esm/utilities/volumeCropping/constants.js +31 -0
  17. package/dist/esm/utilities/volumeCropping/copyClippingPlanes.d.ts +2 -0
  18. package/dist/esm/utilities/volumeCropping/copyClippingPlanes.js +6 -0
  19. package/dist/esm/utilities/volumeCropping/extractVolumeDirectionVectors.d.ts +9 -0
  20. package/dist/esm/utilities/volumeCropping/extractVolumeDirectionVectors.js +9 -0
  21. package/dist/esm/utilities/volumeCropping/findLineBoundsIntersection.d.ts +5 -0
  22. package/dist/esm/utilities/volumeCropping/findLineBoundsIntersection.js +50 -0
  23. package/dist/esm/utilities/volumeCropping/getColorKeyForPlaneIndex.d.ts +1 -0
  24. package/dist/esm/utilities/volumeCropping/getColorKeyForPlaneIndex.js +13 -0
  25. package/dist/esm/utilities/volumeCropping/getOrientationFromNormal.d.ts +2 -0
  26. package/dist/esm/utilities/volumeCropping/getOrientationFromNormal.js +19 -0
  27. package/dist/esm/utilities/volumeCropping/index.d.ts +9 -0
  28. package/dist/esm/utilities/volumeCropping/index.js +9 -0
  29. package/dist/esm/utilities/volumeCropping/parseCornerKey.d.ts +8 -0
  30. package/dist/esm/utilities/volumeCropping/parseCornerKey.js +11 -0
  31. package/dist/esm/utilities/volumeCropping/types.d.ts +5 -0
  32. package/dist/esm/utilities/volumeCropping/types.js +0 -0
  33. package/dist/esm/version.d.ts +1 -1
  34. package/dist/esm/version.js +1 -1
  35. package/package.json +3 -3
@@ -1,25 +1,18 @@
1
- import { vec2, vec3 } from 'gl-matrix';
1
+ import { vec2 } from 'gl-matrix';
2
2
  import vtkMath from '@kitware/vtk.js/Common/Core/Math';
3
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';
4
+ import { getRenderingEngine, getEnabledElementByIds, getEnabledElement, Enums, CONSTANTS, triggerEvent, eventTarget, convertColorArrayToRgbString, } from '@cornerstonejs/core';
5
+ import { getToolGroup } from '../store/ToolGroupManager';
6
6
  import { addAnnotation, getAnnotations, removeAnnotation, } from '../stateManagement/annotation/annotationState';
7
7
  import { drawCircle as drawCircleSvg, drawLine as drawLineSvg, } from '../drawingSvg';
8
8
  import { state } from '../store/state';
9
9
  import { Events } from '../enums';
10
10
  import { getViewportIdsWithToolToRender } from '../utilities/viewportFilters';
11
11
  import { resetElementCursor, hideElementCursor, } from '../cursors/elementCursor';
12
- import liangBarksyClip from '../utilities/math/vec2/liangBarksyClip';
13
12
  import * as lineSegment from '../utilities/math/line';
14
13
  import { isAnnotationLocked } from '../stateManagement/annotation/annotationLocking';
15
14
  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
- }
15
+ import { NUM_CLIPPING_PLANES, LINE_INTERSECTION_TOLERANCE, POINT_PROXIMITY_THRESHOLD_PIXELS, copyClippingPlanes, getColorKeyForPlaneIndex, getOrientationFromNormal, computePlanePlaneIntersection, findLineBoundsIntersection, } from '../utilities/volumeCropping';
23
16
  const OPERATION = {
24
17
  DRAG: 1,
25
18
  ROTATE: 2,
@@ -52,12 +45,7 @@ class VolumeCroppingControlTool extends AnnotationTool {
52
45
  },
53
46
  }) {
54
47
  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];
48
+ this.clippingPlanes = [];
61
49
  this.initializeViewport = ({ renderingEngineId, viewportId, }) => {
62
50
  if (!renderingEngineId || !viewportId) {
63
51
  console.warn('VolumeCroppingControlTool: Missing renderingEngineId or viewportId');
@@ -68,15 +56,21 @@ class VolumeCroppingControlTool extends AnnotationTool {
68
56
  return;
69
57
  }
70
58
  const { viewport } = enabledElement;
71
- this._updateToolCentersFromViewport(viewport);
59
+ const volumeActors = viewport.getActors();
60
+ if (volumeActors && volumeActors.length > 0) {
61
+ const imageData = volumeActors[0].actor.getMapper().getInputData();
62
+ if (imageData) {
63
+ this.seriesInstanceUID = imageData.seriesInstanceUID || 'unknown';
64
+ }
65
+ }
72
66
  const { element } = viewport;
73
- const { position, focalPoint, viewPlaneNormal } = viewport.getCamera();
67
+ const { position, focalPoint } = viewport.getCamera();
74
68
  let annotations = this._getAnnotations(enabledElement);
75
69
  annotations = this.filterInteractableAnnotationsForElement(element, annotations);
76
70
  if (annotations?.length) {
77
71
  removeAnnotation(annotations[0].annotationUID);
78
72
  }
79
- const orientation = this._getOrientationFromNormal(viewport.getCamera().viewPlaneNormal);
73
+ const orientation = getOrientationFromNormal(viewport.getCamera().viewPlaneNormal);
80
74
  const annotation = {
81
75
  highlighted: false,
82
76
  metadata: {
@@ -86,11 +80,11 @@ class VolumeCroppingControlTool extends AnnotationTool {
86
80
  },
87
81
  data: {
88
82
  handles: {
89
- toolCenter: this.toolCenter,
90
- toolCenterMin: this.toolCenterMin,
91
- toolCenterMax: this.toolCenterMax,
83
+ activeOperation: null,
84
+ clippingPlanes: this.clippingPlanes.length > 0
85
+ ? copyClippingPlanes(this.clippingPlanes)
86
+ : [],
92
87
  },
93
- activeOperation: null,
94
88
  activeViewportIds: [],
95
89
  viewportId,
96
90
  referenceLines: [],
@@ -98,10 +92,6 @@ class VolumeCroppingControlTool extends AnnotationTool {
98
92
  },
99
93
  };
100
94
  addAnnotation(annotation, element);
101
- return {
102
- normal: viewPlaneNormal,
103
- point: viewport.canvasToWorld([100, 100]),
104
- };
105
95
  };
106
96
  this._getViewportsInfo = () => {
107
97
  const viewports = getToolGroup(this.toolGroupId).viewportsInfo;
@@ -134,139 +124,22 @@ class VolumeCroppingControlTool extends AnnotationTool {
134
124
  }
135
125
  viewport.render();
136
126
  }
137
- this._computeToolCenter(viewportsInfo);
138
- };
139
- this.computeToolCenter = () => {
140
- const viewportsInfo = this._getViewportsInfo();
127
+ this._initializeViewports(viewportsInfo);
141
128
  };
142
- this._computeToolCenter = (viewportsInfo) => {
143
- if (!viewportsInfo || !viewportsInfo[0]) {
144
- console.warn(' _computeToolCenter : No valid viewportsInfo for computeToolCenter.');
129
+ this._initializeViewports = (viewportsInfo) => {
130
+ if (!viewportsInfo?.length || !viewportsInfo[0]) {
131
+ console.warn('VolumeCroppingControlTool: No valid viewportsInfo for initialization.');
145
132
  return;
146
133
  }
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);
134
+ viewportsInfo.forEach((vpInfo) => {
135
+ this.initializeViewport(vpInfo);
176
136
  });
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
- }
137
+ triggerAnnotationRenderForViewportIds(viewportsInfo.map(({ viewportId }) => viewportId));
264
138
  };
265
139
  this.cancel = () => {
266
- console.log('Not implemented yet');
267
140
  };
268
141
  this.isPointNearTool = (element, annotation, canvasCoords, proximity) => {
269
- if (this._pointNearTool(element, annotation, canvasCoords, 6)) {
142
+ if (this._pointNearTool(element, annotation, canvasCoords, POINT_PROXIMITY_THRESHOLD_PIXELS)) {
270
143
  return true;
271
144
  }
272
145
  return false;
@@ -298,13 +171,8 @@ class VolumeCroppingControlTool extends AnnotationTool {
298
171
  if (!data.handles) {
299
172
  continue;
300
173
  }
301
- const previousActiveOperation = data.handles.activeOperation;
302
- const previousActiveViewportIds = data.activeViewportIds && data.activeViewportIds.length > 0
303
- ? [...data.activeViewportIds]
304
- : [];
305
174
  data.activeViewportIds = [];
306
- let near = false;
307
- near = this._pointNearTool(element, annotation, canvasCoords, 6);
175
+ const near = this._pointNearTool(element, annotation, canvasCoords, POINT_PROXIMITY_THRESHOLD_PIXELS);
308
176
  const nearToolAndNotMarkedActive = near && !highlighted;
309
177
  const notNearToolAndMarkedActive = !near && highlighted;
310
178
  if (nearToolAndNotMarkedActive || notNearToolAndMarkedActive) {
@@ -321,12 +189,9 @@ class VolumeCroppingControlTool extends AnnotationTool {
321
189
  const enabledElement = getEnabledElement(element);
322
190
  let orientation = null;
323
191
  if (enabledElement.viewport && enabledElement.viewport.getCamera) {
324
- orientation = this._getOrientationFromNormal(enabledElement.viewport.getCamera().viewPlaneNormal);
192
+ orientation = getOrientationFromNormal(enabledElement.viewport.getCamera().viewPlaneNormal);
325
193
  }
326
194
  const filtered = annotations.filter((annotation) => {
327
- if (annotation.isVirtual) {
328
- return true;
329
- }
330
195
  if (annotation.data.orientation &&
331
196
  orientation &&
332
197
  annotation.data.orientation === orientation) {
@@ -343,7 +208,7 @@ class VolumeCroppingControlTool extends AnnotationTool {
343
208
  const s2_x = q2[0] - q1[0];
344
209
  const s2_y = q2[1] - q1[1];
345
210
  const denom = -s2_x * s1_y + s1_x * s2_y;
346
- if (Math.abs(denom) < 1e-8) {
211
+ if (Math.abs(denom) < LINE_INTERSECTION_TOLERANCE) {
347
212
  return null;
348
213
  }
349
214
  const s = (-s1_y * (p1[0] - q1[0]) + s1_x * (p1[1] - q1[1])) / denom;
@@ -360,10 +225,7 @@ class VolumeCroppingControlTool extends AnnotationTool {
360
225
  let renderStatus = false;
361
226
  const { viewport, renderingEngine } = enabledElement;
362
227
  const { element } = viewport;
363
- let annotations = this._getAnnotations(enabledElement);
364
- if (this._virtualAnnotations && this._virtualAnnotations.length) {
365
- annotations = annotations.concat(this._virtualAnnotations);
366
- }
228
+ const annotations = this._getAnnotations(enabledElement);
367
229
  const camera = viewport.getCamera();
368
230
  const filteredToolAnnotations = this.filterInteractableAnnotationsForElement(element, annotations);
369
231
  const viewportAnnotation = filteredToolAnnotations[0];
@@ -374,135 +236,65 @@ class VolumeCroppingControlTool extends AnnotationTool {
374
236
  const { clientWidth, clientHeight } = viewport.canvas;
375
237
  const canvasDiagonalLength = Math.sqrt(clientWidth * clientWidth + clientHeight * clientHeight);
376
238
  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
- }
239
+ let clippingPlanes = viewportAnnotation.data.handles.clippingPlanes;
240
+ if (!clippingPlanes || clippingPlanes.length < NUM_CLIPPING_PLANES) {
241
+ if (this.clippingPlanes &&
242
+ this.clippingPlanes.length >= NUM_CLIPPING_PLANES) {
243
+ clippingPlanes = this.clippingPlanes;
244
+ data.handles.clippingPlanes = copyClippingPlanes(this.clippingPlanes);
435
245
  }
436
246
  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);
247
+ return false;
248
+ }
249
+ }
250
+ const { viewPlaneNormal, focalPoint } = camera;
251
+ const referenceLines = [];
252
+ const planeTypes = [
253
+ 'min',
254
+ 'max',
255
+ 'min',
256
+ 'max',
257
+ 'min',
258
+ 'max',
259
+ ];
260
+ for (let planeIndex = 0; planeIndex < NUM_CLIPPING_PLANES; planeIndex++) {
261
+ const clippingPlane = clippingPlanes[planeIndex];
262
+ const intersection = computePlanePlaneIntersection(clippingPlane, viewPlaneNormal, focalPoint);
263
+ if (!intersection) {
264
+ continue;
265
+ }
266
+ const lineBounds = findLineBoundsIntersection(intersection.point, intersection.direction, viewport);
267
+ if (!lineBounds) {
268
+ continue;
445
269
  }
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
270
  referenceLines.push([
489
- otherViewport,
490
- refLinePointMaxOne,
491
- refLinePointMaxTwo,
492
- 'max',
271
+ {
272
+ id: viewport.id,
273
+ canvas: viewport.canvas,
274
+ },
275
+ lineBounds.start,
276
+ lineBounds.end,
277
+ planeTypes[planeIndex],
278
+ planeIndex,
493
279
  ]);
494
- });
280
+ }
495
281
  data.referenceLines = referenceLines;
496
282
  const viewportColor = this._getReferenceLineColor(viewport.id);
497
- const color = viewportColor !== undefined ? viewportColor : 'rgb(200, 200, 200)';
283
+ const defaultColor = viewportColor !== undefined ? viewportColor : 'rgb(200, 200, 200)';
498
284
  referenceLines.forEach((line, lineIndex) => {
285
+ const [otherViewport, startPoint, endPoint, type, planeIndex] = line;
286
+ if (planeIndex === undefined ||
287
+ planeIndex < 0 ||
288
+ planeIndex >= NUM_CLIPPING_PLANES) {
289
+ return;
290
+ }
499
291
  const intersections = [];
500
292
  for (let j = 0; j < referenceLines.length; ++j) {
501
293
  if (j === lineIndex) {
502
294
  continue;
503
295
  }
504
296
  const otherLine = referenceLines[j];
505
- const intersection = lineIntersection2D(line[1], line[2], otherLine[1], otherLine[2]);
297
+ const intersection = lineIntersection2D(startPoint, endPoint, otherLine[1], otherLine[2]);
506
298
  if (intersection) {
507
299
  intersections.push({
508
300
  with: otherLine[3],
@@ -510,32 +302,12 @@ class VolumeCroppingControlTool extends AnnotationTool {
510
302
  });
511
303
  }
512
304
  }
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
- }
305
+ const colorKey = getColorKeyForPlaneIndex(planeIndex);
533
306
  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;
307
+ const colorArr = colorKey
308
+ ? lineColors[colorKey] || lineColors.UNKNOWN || [1.0, 0.0, 0.0]
309
+ : [1.0, 0.0, 0.0];
310
+ const color = convertColorArrayToRgbString(colorArr);
539
311
  const viewportControllable = this._getReferenceLineControllable(otherViewport.id);
540
312
  const selectedViewportId = data.activeViewportIds.find((id) => id === otherViewport.id);
541
313
  let lineWidth = this.configuration.lineWidth ?? 1.5;
@@ -545,28 +317,29 @@ class VolumeCroppingControlTool extends AnnotationTool {
545
317
  if (lineActive) {
546
318
  lineWidth = this.configuration.activeLineWidth ?? 2.5;
547
319
  }
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
- }
320
+ const lineUID = `plane_${planeIndex}`;
321
+ if (intersections.length === 2) {
322
+ drawLineSvg(svgDrawingHelper, annotationUID, lineUID, intersections[0].point, intersections[1].point, {
323
+ color,
324
+ lineWidth,
325
+ });
326
+ }
327
+ else {
328
+ drawLineSvg(svgDrawingHelper, annotationUID, lineUID, startPoint, endPoint, {
329
+ color,
330
+ lineWidth,
331
+ });
332
+ }
333
+ if (this.configuration.extendReferenceLines &&
334
+ intersections.length === 2) {
335
+ const sortedIntersections = intersections
336
+ .map((intersection) => ({
337
+ ...intersection,
338
+ distance: vec2.distance(startPoint, intersection.point),
339
+ }))
340
+ .sort((a, b) => a.distance - b.distance);
341
+ drawLineSvg(svgDrawingHelper, annotationUID, lineUID + '_dashed_before', startPoint, sortedIntersections[0].point, { color, lineWidth, lineDash: [4, 4] });
342
+ drawLineSvg(svgDrawingHelper, annotationUID, lineUID + '_dashed_after', sortedIntersections[1].point, endPoint, { color, lineWidth, lineDash: [4, 4] });
570
343
  }
571
344
  });
572
345
  renderStatus = true;
@@ -580,7 +353,7 @@ class VolumeCroppingControlTool extends AnnotationTool {
580
353
  ];
581
354
  const circleRadius = viewportIndicatorsConfig?.circleRadius || canvasDiagonalLength * 0.01;
582
355
  const circleUID = '0';
583
- drawCircleSvg(svgDrawingHelper, annotationUID, circleUID, referenceColorCoordinates, circleRadius, { color, fill: color });
356
+ drawCircleSvg(svgDrawingHelper, annotationUID, circleUID, referenceColorCoordinates, circleRadius, { color: defaultColor, fill: defaultColor });
584
357
  }
585
358
  return renderStatus;
586
359
  };
@@ -602,40 +375,7 @@ class VolumeCroppingControlTool extends AnnotationTool {
602
375
  if (evt.detail.seriesInstanceUID !== this.seriesInstanceUID) {
603
376
  return;
604
377
  }
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
- }
378
+ return;
639
379
  }
640
380
  };
641
381
  this._onNewVolume = () => {
@@ -643,72 +383,28 @@ class VolumeCroppingControlTool extends AnnotationTool {
643
383
  if (viewportsInfo && viewportsInfo.length > 0) {
644
384
  const { viewportId, renderingEngineId } = viewportsInfo[0];
645
385
  const renderingEngine = getRenderingEngine(renderingEngineId);
386
+ if (!renderingEngine) {
387
+ return;
388
+ }
646
389
  const viewport = renderingEngine.getViewport(viewportId);
390
+ if (!viewport) {
391
+ return;
392
+ }
647
393
  const volumeActors = viewport.getActors();
648
394
  if (volumeActors.length > 0) {
649
395
  const imageData = volumeActors[0].actor.getMapper().getInputData();
650
396
  if (imageData) {
651
397
  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
398
  }
660
399
  }
661
400
  }
662
- this._computeToolCenter(viewportsInfo);
401
+ this._initializeViewports(viewportsInfo);
663
402
  triggerEvent(eventTarget, Events.VOLUMECROPPINGCONTROL_TOOL_CHANGED, {
664
403
  toolGroupId: this.toolGroupId,
665
404
  viewportsInfo: viewportsInfo,
666
405
  seriesInstanceUID: this.seriesInstanceUID,
667
406
  });
668
407
  };
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
408
  this._activateModify = (element) => {
713
409
  state.isInteractingWithTool = !this.configuration.mobile?.enabled;
714
410
  element.addEventListener(Events.MOUSE_UP, this._endCallback);
@@ -760,48 +456,71 @@ class VolumeCroppingControlTool extends AnnotationTool {
760
456
  return;
761
457
  }
762
458
  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];
459
+ const clippingPlanes = handles.clippingPlanes;
460
+ if (handles.activeOperation === OPERATION.DRAG &&
461
+ clippingPlanes &&
462
+ clippingPlanes.length >= NUM_CLIPPING_PLANES) {
463
+ if (handles.activePlaneIndex !== undefined &&
464
+ handles.activePlaneIndex >= 0 &&
465
+ handles.activePlaneIndex < NUM_CLIPPING_PLANES) {
466
+ const planeIndex = handles.activePlaneIndex;
467
+ const plane = clippingPlanes[planeIndex];
468
+ const normal = plane.normal;
469
+ const dotProd = vtkMath.dot(delta, normal);
470
+ const moveDistance = dotProd;
471
+ plane.origin[0] += normal[0] * moveDistance;
472
+ plane.origin[1] += normal[1] * moveDistance;
473
+ plane.origin[2] += normal[2] * moveDistance;
768
474
  }
769
- else if (handles.activeType === 'max') {
770
- this.toolCenterMax[0] += delta[0];
771
- this.toolCenterMax[1] += delta[1];
772
- this.toolCenterMax[2] += delta[2];
475
+ else if (handles.activeType === 'min') {
476
+ clippingPlanes[0].origin[0] += delta[0];
477
+ clippingPlanes[2].origin[1] += delta[1];
478
+ clippingPlanes[4].origin[2] += delta[2];
773
479
  }
774
- else {
775
- this.toolCenter[0] += delta[0];
776
- this.toolCenter[1] += delta[1];
777
- this.toolCenter[2] += delta[2];
480
+ else if (handles.activeType === 'max') {
481
+ clippingPlanes[1].origin[0] += delta[0];
482
+ clippingPlanes[3].origin[1] += delta[1];
483
+ clippingPlanes[5].origin[2] += delta[2];
778
484
  }
485
+ this.clippingPlanes = copyClippingPlanes(clippingPlanes);
779
486
  const viewportsInfo = this._getViewportsInfo();
487
+ viewportsInfo.forEach(({ viewportId, renderingEngineId }) => {
488
+ const enabledElement = getEnabledElementByIds(viewportId, renderingEngineId);
489
+ if (enabledElement) {
490
+ const annotations = this._getAnnotations(enabledElement);
491
+ annotations.forEach((annotation) => {
492
+ if (annotation.data?.handles) {
493
+ annotation.data.handles.clippingPlanes = copyClippingPlanes(this.clippingPlanes);
494
+ }
495
+ });
496
+ }
497
+ });
780
498
  triggerAnnotationRenderForViewportIds(viewportsInfo.map(({ viewportId }) => viewportId));
781
499
  triggerEvent(eventTarget, Events.VOLUMECROPPINGCONTROL_TOOL_CHANGED, {
782
500
  toolGroupId: this.toolGroupId,
783
- toolCenter: this.toolCenter,
784
- toolCenterMin: this.toolCenterMin,
785
- toolCenterMax: this.toolCenterMax,
501
+ clippingPlanes: this.clippingPlanes,
786
502
  handleType: handles.activeType,
787
- viewportOrientation: [],
788
503
  seriesInstanceUID: this.seriesInstanceUID,
789
504
  });
790
505
  }
791
506
  };
792
507
  this._getReferenceLineColor =
793
508
  toolProps.configuration?.getReferenceLineColor ||
794
- defaultReferenceLineColor;
509
+ (() => 'rgb(0, 200, 0)');
795
510
  this._getReferenceLineControllable =
796
- toolProps.configuration?.getReferenceLineControllable ||
797
- defaultReferenceLineControllable;
511
+ toolProps.configuration?.getReferenceLineControllable || (() => true);
798
512
  const viewportsInfo = getToolGroup(this.toolGroupId)?.viewportsInfo;
799
513
  eventTarget.addEventListener(Events.VOLUMECROPPING_TOOL_CHANGED, this._onSphereMoved);
800
514
  if (viewportsInfo && viewportsInfo.length > 0) {
801
515
  const { viewportId, renderingEngineId } = viewportsInfo[0];
802
- const enabledElement = getEnabledElementByIds(viewportId, renderingEngineId);
803
516
  const renderingEngine = getRenderingEngine(renderingEngineId);
517
+ if (!renderingEngine) {
518
+ return;
519
+ }
804
520
  const viewport = renderingEngine.getViewport(viewportId);
521
+ if (!viewport) {
522
+ return;
523
+ }
805
524
  const volumeActors = viewport.getActors();
806
525
  if (!volumeActors || !volumeActors.length) {
807
526
  console.warn(`VolumeCroppingControlTool: No volume actors found in viewport ${viewportId}.`);
@@ -809,67 +528,11 @@ class VolumeCroppingControlTool extends AnnotationTool {
809
528
  }
810
529
  const imageData = volumeActors[0].actor.getMapper().getInputData();
811
530
  if (imageData) {
812
- const dimensions = imageData.getDimensions();
813
- const spacing = imageData.getSpacing();
814
- const origin = imageData.getOrigin();
815
531
  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
532
  }
834
533
  }
835
534
  }
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
535
  onSetToolInactive() {
872
- console.debug(`VolumeCroppingControlTool: onSetToolInactive called for tool ${this.getToolName()}`);
873
536
  }
874
537
  onSetToolActive() {
875
538
  const viewportsInfo = this._getViewportsInfo();
@@ -885,7 +548,7 @@ class VolumeCroppingControlTool extends AnnotationTool {
885
548
  if (!anyAnnotationExists) {
886
549
  this._unsubscribeToViewportNewVolumeSet(viewportsInfo);
887
550
  this._subscribeToViewportNewVolumeSet(viewportsInfo);
888
- this._computeToolCenter(viewportsInfo);
551
+ this._initializeViewports(viewportsInfo);
889
552
  triggerEvent(eventTarget, Events.VOLUMECROPPINGCONTROL_TOOL_CHANGED, {
890
553
  toolGroupId: this.toolGroupId,
891
554
  viewportsInfo: viewportsInfo,
@@ -909,13 +572,12 @@ class VolumeCroppingControlTool extends AnnotationTool {
909
572
  }
910
573
  }
911
574
  onSetToolEnabled() {
912
- console.debug(`VolumeCroppingControlTool: onSetToolEnabled called for tool ${this.getToolName()}`);
913
- const viewportsInfo = this._getViewportsInfo();
575
+ eventTarget.addEventListener(Events.VOLUMECROPPING_TOOL_CHANGED, this._onSphereMoved);
914
576
  }
915
577
  onSetToolDisabled() {
916
- console.debug(`VolumeCroppingControlTool: onSetToolDisabled called for tool ${this.getToolName()}`);
917
578
  const viewportsInfo = this._getViewportsInfo();
918
579
  this._unsubscribeToViewportNewVolumeSet(viewportsInfo);
580
+ eventTarget.removeEventListener(Events.VOLUMECROPPING_TOOL_CHANGED, this._onSphereMoved);
919
581
  viewportsInfo.forEach(({ renderingEngineId, viewportId }) => {
920
582
  const enabledElement = getEnabledElementByIds(viewportId, renderingEngineId);
921
583
  if (!enabledElement) {
@@ -929,176 +591,24 @@ class VolumeCroppingControlTool extends AnnotationTool {
929
591
  }
930
592
  });
931
593
  }
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
594
  _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
- ];
595
+ if (!originalClippingPlanes ||
596
+ originalClippingPlanes.length < NUM_CLIPPING_PLANES) {
597
+ return;
598
+ }
599
+ this.clippingPlanes = copyClippingPlanes(originalClippingPlanes);
600
+ const viewportsInfo = this._getViewportsInfo();
601
+ viewportsInfo.forEach(({ viewportId, renderingEngineId }) => {
602
+ const enabledElement = getEnabledElementByIds(viewportId, renderingEngineId);
603
+ if (enabledElement) {
604
+ const annotations = this._getAnnotations(enabledElement);
605
+ annotations.forEach((annotation) => {
606
+ if (annotation.data?.handles) {
607
+ annotation.data.handles.clippingPlanes = copyClippingPlanes(this.clippingPlanes);
1088
608
  }
1089
609
  });
1090
610
  }
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();
611
+ });
1102
612
  triggerAnnotationRenderForViewportIds(viewportsInfo.map(({ viewportId }) => viewportId));
1103
613
  }
1104
614
  addNewAnnotation(evt) {
@@ -1123,7 +633,6 @@ class VolumeCroppingControlTool extends AnnotationTool {
1123
633
  continue;
1124
634
  }
1125
635
  viewportIdArray.push(otherViewport.id);
1126
- i++;
1127
636
  }
1128
637
  data.activeViewportIds = [...viewportIdArray];
1129
638
  data.handles.activeOperation = OPERATION.DRAG;
@@ -1149,51 +658,22 @@ class VolumeCroppingControlTool extends AnnotationTool {
1149
658
  element.addEventListener(Enums.Events.VOLUME_VIEWPORT_NEW_VOLUME, this._onNewVolume);
1150
659
  });
1151
660
  }
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
661
  _pointNearTool(element, annotation, canvasCoords, proximity) {
1180
662
  const { data } = annotation;
1181
663
  const referenceLines = data.referenceLines;
1182
664
  const viewportIdArray = [];
1183
665
  if (referenceLines) {
1184
666
  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, [
667
+ const [otherViewport, startPoint, endPoint, type, planeIndex] = referenceLines[i];
668
+ const distance = lineSegment.distanceToPoint(startPoint, endPoint, [
1190
669
  canvasCoords[0],
1191
670
  canvasCoords[1],
1192
671
  ]);
1193
- if (distance1 <= proximity) {
672
+ if (distance <= proximity) {
1194
673
  viewportIdArray.push(otherViewport.id);
1195
- data.handles.activeOperation = 1;
674
+ data.handles.activeOperation = OPERATION.DRAG;
1196
675
  data.handles.activeType = type;
676
+ data.handles.activePlaneIndex = planeIndex;
1197
677
  }
1198
678
  }
1199
679
  }
@@ -1201,7 +681,7 @@ class VolumeCroppingControlTool extends AnnotationTool {
1201
681
  this.editData = {
1202
682
  annotation,
1203
683
  };
1204
- return data.handles.activeOperation === 1 ? true : false;
684
+ return data.handles.activeOperation === OPERATION.DRAG ? true : false;
1205
685
  }
1206
686
  }
1207
687
  VolumeCroppingControlTool.toolName = 'VolumeCroppingControl';