@cornerstonejs/tools 5.6.14 → 5.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/esm/eventDispatchers/shared/getTouchCallbackWithMouseFallback.d.ts +1 -0
  2. package/dist/esm/eventDispatchers/shared/getTouchCallbackWithMouseFallback.js +24 -0
  3. package/dist/esm/eventDispatchers/shared/releaseToolForMultiTouchGesture.d.ts +2 -0
  4. package/dist/esm/eventDispatchers/shared/releaseToolForMultiTouchGesture.js +30 -0
  5. package/dist/esm/eventDispatchers/touchEventHandlers/touchDrag.js +7 -4
  6. package/dist/esm/eventDispatchers/touchEventHandlers/touchStart.js +7 -4
  7. package/dist/esm/eventDispatchers/touchEventHandlers/touchStartActivate.js +7 -2
  8. package/dist/esm/eventDispatchers/touchEventHandlers/touchTap.d.ts +2 -2
  9. package/dist/esm/eventDispatchers/touchEventHandlers/touchTap.js +29 -3
  10. package/dist/esm/eventDispatchers/touchToolEventDispatcher.js +1 -0
  11. package/dist/esm/eventListeners/touch/touchStartListener.js +7 -2
  12. package/dist/esm/store/ToolGroupManager/ToolGroup.js +1 -1
  13. package/dist/esm/store/filterMoveableAnnotationTools.js +2 -1
  14. package/dist/esm/store/filterToolsWithMoveableHandles.js +2 -1
  15. package/dist/esm/tools/AdvancedMagnifyTool.d.ts +11 -4
  16. package/dist/esm/tools/AdvancedMagnifyTool.js +141 -7
  17. package/dist/esm/tools/CrosshairsTool.d.ts +1 -1
  18. package/dist/esm/tools/CrosshairsTool.js +9 -8
  19. package/dist/esm/tools/MagnifyTool.d.ts +2 -0
  20. package/dist/esm/tools/MagnifyTool.js +19 -4
  21. package/dist/esm/tools/TrackballRotateTool.js +7 -0
  22. package/dist/esm/tools/VolumeCroppingTool.js +8 -0
  23. package/dist/esm/tools/VolumeRotateTool.d.ts +5 -1
  24. package/dist/esm/tools/VolumeRotateTool.js +24 -3
  25. package/dist/esm/tools/annotation/ArrowAnnotateTool.d.ts +1 -1
  26. package/dist/esm/tools/annotation/ArrowAnnotateTool.js +3 -3
  27. package/dist/esm/tools/annotation/ClickSegmentTool.d.ts +1 -0
  28. package/dist/esm/tools/annotation/ClickSegmentTool.js +14 -0
  29. package/dist/esm/tools/annotation/DragProbeTool.js +3 -1
  30. package/dist/esm/tools/annotation/LivewireContourTool.d.ts +3 -0
  31. package/dist/esm/tools/annotation/LivewireContourTool.js +36 -2
  32. package/dist/esm/tools/annotation/ProbeTool.d.ts +10 -2
  33. package/dist/esm/tools/annotation/ProbeTool.js +22 -3
  34. package/dist/esm/tools/annotation/RegionSegmentTool.js +6 -0
  35. package/dist/esm/tools/annotation/SplineROITool.d.ts +4 -0
  36. package/dist/esm/tools/annotation/SplineROITool.js +41 -3
  37. package/dist/esm/tools/annotation/WholeBodySegmentTool.js +7 -1
  38. package/dist/esm/tools/base/BaseTool.d.ts +7 -1
  39. package/dist/esm/tools/base/BaseTool.js +16 -0
  40. package/dist/esm/tools/base/ContourSegmentationBaseTool.d.ts +4 -0
  41. package/dist/esm/tools/base/ContourSegmentationBaseTool.js +23 -0
  42. package/dist/esm/tools/segmentation/BrushTool.d.ts +3 -0
  43. package/dist/esm/tools/segmentation/BrushTool.js +62 -4
  44. package/dist/esm/tools/segmentation/CircleScissorsTool.js +6 -1
  45. package/dist/esm/tools/segmentation/LabelmapBaseTool.d.ts +5 -0
  46. package/dist/esm/tools/segmentation/LabelmapBaseTool.js +15 -0
  47. package/dist/esm/tools/segmentation/RectangleROIStartEndThresholdTool.js +1 -0
  48. package/dist/esm/tools/segmentation/RectangleScissorsTool.js +6 -1
  49. package/dist/esm/tools/segmentation/SegmentLabelTool.js +1 -1
  50. package/dist/esm/tools/segmentation/SegmentSelectTool.js +1 -1
  51. package/dist/esm/tools/segmentation/SphereScissorsTool.js +6 -1
  52. package/dist/esm/utilities/touch/constants.d.ts +4 -0
  53. package/dist/esm/utilities/touch/constants.js +4 -0
  54. package/dist/esm/utilities/touch/index.d.ts +3 -1
  55. package/dist/esm/utilities/touch/index.js +7 -1
  56. package/dist/esm/version.d.ts +1 -1
  57. package/dist/esm/version.js +1 -1
  58. package/package.json +4 -4
@@ -14,6 +14,7 @@ import getViewportICamera from '../utilities/getViewportICamera.js';
14
14
  import { resetElementCursor, hideElementCursor, } from '../cursors/elementCursor.js';
15
15
  import liangBarksyClip from '../utilities/math/vec2/liangBarksyClip.js';
16
16
  import { getSlabThicknessOrDefault, jumpToFocalPoint, } from '../utilities/genericViewportToolHelpers.js';
17
+ import { isMobile } from '../utilities/touch/index.js';
17
18
  import * as lineSegment from '../utilities/math/line/index.js';
18
19
  import { isAnnotationLocked } from '../stateManagement/annotation/annotationLocking.js';
19
20
  import triggerAnnotationRenderForViewportIds from '../utilities/triggerAnnotationRenderForViewportIds.js';
@@ -48,7 +49,7 @@ const OPERATION = {
48
49
  };
49
50
  class CrosshairsTool extends AnnotationTool {
50
51
  constructor(toolProps = {}, defaultToolProps = {
51
- supportedInteractionTypes: ['Mouse'],
52
+ supportedInteractionTypes: ['Mouse', 'Touch'],
52
53
  configuration: {
53
54
  shadow: true,
54
55
  viewportIndicators: false,
@@ -77,7 +78,7 @@ class CrosshairsTool extends AnnotationTool {
77
78
  size: 2,
78
79
  },
79
80
  mobile: {
80
- enabled: false,
81
+ enabled: isMobile(),
81
82
  opacity: 0.8,
82
83
  handleRadius: 9,
83
84
  referenceLinesCenterGapRatio: 0.05,
@@ -231,7 +232,7 @@ class CrosshairsTool extends AnnotationTool {
231
232
  data.handles.activeOperation = OPERATION.DRAG;
232
233
  evt.preventDefault();
233
234
  hideElementCursor(element);
234
- this._activateModify(element);
235
+ this._activateModify(element, evt);
235
236
  return filteredAnnotations[0];
236
237
  };
237
238
  this.cancel = () => {
@@ -241,12 +242,12 @@ class CrosshairsTool extends AnnotationTool {
241
242
  const eventDetail = evt.detail;
242
243
  const { element } = eventDetail;
243
244
  annotation.highlighted = true;
244
- this._activateModify(element);
245
+ this._activateModify(element, evt);
245
246
  hideElementCursor(element);
246
247
  evt.preventDefault();
247
248
  };
248
249
  this.isPointNearTool = (element, annotation, canvasCoords, proximity) => {
249
- if (this._pointNearTool(element, annotation, canvasCoords, 6)) {
250
+ if (this._pointNearTool(element, annotation, canvasCoords, proximity)) {
250
251
  return true;
251
252
  }
252
253
  return false;
@@ -255,7 +256,7 @@ class CrosshairsTool extends AnnotationTool {
255
256
  const eventDetail = evt.detail;
256
257
  const { element } = eventDetail;
257
258
  annotation.highlighted = true;
258
- this._activateModify(element);
259
+ this._activateModify(element, evt);
259
260
  hideElementCursor(element);
260
261
  evt.preventDefault();
261
262
  };
@@ -959,13 +960,13 @@ class CrosshairsTool extends AnnotationTool {
959
960
  state.isInteractingWithTool = false;
960
961
  return true;
961
962
  };
962
- this._activateModify = (element) => {
963
+ this._activateModify = (element, evt) => {
963
964
  this._syncVolumeListenersWithToolGroup();
964
965
  this._recomputeToolCenterFromAbsoluteCameras({
965
966
  emitEvent: false,
966
967
  updateViewportCameras: false,
967
968
  });
968
- state.isInteractingWithTool = !this.configuration.mobile?.enabled;
969
+ state.isInteractingWithTool = !(this._isTouchInteraction(evt) && this.configuration.mobile?.enabled);
969
970
  element.addEventListener(Events.MOUSE_UP, this._endCallback);
970
971
  element.addEventListener(Events.MOUSE_DRAG, this._dragCallback);
971
972
  element.addEventListener(Events.MOUSE_CLICK, this._endCallback);
@@ -9,12 +9,14 @@ declare class MagnifyTool extends BaseTool {
9
9
  enabledElement: Types.IEnabledElement;
10
10
  renderingEngine: Types.IRenderingEngine;
11
11
  currentPoints: IPoints;
12
+ isTouch: boolean;
12
13
  } | null;
13
14
  constructor(toolProps?: PublicToolProps, defaultToolProps?: ToolProps);
14
15
  private _hasBeenRemoved;
15
16
  _getReferencedImageId(viewport: Types.IViewport): string | undefined;
16
17
  preMouseDownCallback: (evt: EventTypes.InteractionEventType) => boolean;
17
18
  preTouchStartCallback: (evt: EventTypes.InteractionEventType) => void;
19
+ private _positionMagnifyElement;
18
20
  _createMagnificationViewport: () => void;
19
21
  _cancelCallback: (evt: EventTypes.InteractionEventType) => void;
20
22
  _dragCallback: (evt: EventTypes.InteractionEventType) => void;
@@ -15,6 +15,7 @@ class MagnifyTool extends BaseTool {
15
15
  magnifySize: 10,
16
16
  magnifyWidth: 250,
17
17
  magnifyHeight: 250,
18
+ touchOffset: 40,
18
19
  },
19
20
  }) {
20
21
  super(toolProps, defaultToolProps);
@@ -33,12 +34,14 @@ class MagnifyTool extends BaseTool {
33
34
  throw new Error('MagnifyTool: No referenced image id found, reconstructed planes not supported yet');
34
35
  }
35
36
  const viewportIdsToRender = getViewportIdsWithToolToRender(element, this.getToolName());
37
+ const isTouch = evt.type === Events.TOUCH_START;
36
38
  this.editData = {
37
39
  referencedImageId,
38
40
  viewportIdsToRender,
39
41
  enabledElement,
40
42
  renderingEngine,
41
43
  currentPoints,
44
+ isTouch,
42
45
  };
43
46
  this._createMagnificationViewport();
44
47
  this._activateDraw(element);
@@ -50,6 +53,20 @@ class MagnifyTool extends BaseTool {
50
53
  this.preTouchStartCallback = (evt) => {
51
54
  this.preMouseDownCallback(evt);
52
55
  };
56
+ this._positionMagnifyElement = (magnifyElement, canvasPos, element) => {
57
+ const { magnifyWidth, magnifyHeight, touchOffset } = this.configuration;
58
+ let left = canvasPos[0] - magnifyWidth / 2;
59
+ let top = canvasPos[1] - magnifyHeight / 2;
60
+ if (this.editData?.isTouch) {
61
+ top = canvasPos[1] - magnifyHeight - (touchOffset ?? 0);
62
+ const maxLeft = Math.max(element.clientWidth - magnifyWidth, 0);
63
+ const maxTop = Math.max(element.clientHeight - magnifyHeight, 0);
64
+ left = Math.min(Math.max(left, 0), maxLeft);
65
+ top = Math.min(Math.max(top, 0), maxTop);
66
+ }
67
+ magnifyElement.style.top = `${top}px`;
68
+ magnifyElement.style.left = `${left}px`;
69
+ };
53
70
  this._createMagnificationViewport = () => {
54
71
  const { enabledElement, referencedImageId, viewportIdsToRender, renderingEngine, currentPoints, } = this.editData;
55
72
  const { viewport } = enabledElement;
@@ -81,8 +98,7 @@ class MagnifyTool extends BaseTool {
81
98
  };
82
99
  renderingEngine.enableElement(viewportInput);
83
100
  }
84
- magnifyToolElement.style.top = `${canvasPos[1] - this.configuration.magnifyHeight / 2}px`;
85
- magnifyToolElement.style.left = `${canvasPos[0] - this.configuration.magnifyWidth / 2}px`;
101
+ this._positionMagnifyElement(magnifyToolElement, canvasPos, element);
86
102
  const magnifyViewport = renderingEngine.getViewport(MAGNIFY_VIEWPORT_ID);
87
103
  magnifyViewport.setStack([referencedImageId]).then(() => {
88
104
  if (this._hasBeenRemoved) {
@@ -146,8 +162,7 @@ class MagnifyTool extends BaseTool {
146
162
  if (!magnifyElement) {
147
163
  return;
148
164
  }
149
- magnifyElement.style.top = `${canvasPos[1] - this.configuration.magnifyHeight / 2}px`;
150
- magnifyElement.style.left = `${canvasPos[0] - this.configuration.magnifyWidth / 2}px`;
165
+ this._positionMagnifyElement(magnifyElement, canvasPos, element);
151
166
  const { focalPoint, position } = magnifyViewport.getCamera();
152
167
  const updatedPosition = [
153
168
  position[0] + deltaPointsWorld[0],
@@ -37,13 +37,20 @@ class TrackballRotateTool extends BaseTool {
37
37
  this._hasResolutionChanged = true;
38
38
  if (this.cleanUp !== null) {
39
39
  document.removeEventListener('mouseup', this.cleanUp);
40
+ document.removeEventListener('touchend', this.cleanUp);
41
+ document.removeEventListener('touchcancel', this.cleanUp);
40
42
  }
41
43
  this.cleanUp = () => {
44
+ document.removeEventListener('mouseup', this.cleanUp);
45
+ document.removeEventListener('touchend', this.cleanUp);
46
+ document.removeEventListener('touchcancel', this.cleanUp);
42
47
  mapper.setSampleDistance(originalSampleDistance);
43
48
  viewport.render();
44
49
  this._hasResolutionChanged = false;
45
50
  };
46
51
  document.addEventListener('mouseup', this.cleanUp, { once: true });
52
+ document.addEventListener('touchend', this.cleanUp, { once: true });
53
+ document.addEventListener('touchcancel', this.cleanUp, { once: true });
47
54
  }
48
55
  return true;
49
56
  };
@@ -17,6 +17,7 @@ import { isDragOwnedBy } from '../utilities/interactionDragCoordinator.js';
17
17
  import { applyViewportPresentation, getViewportPresentation, } from '../utilities/viewportPresentation.js';
18
18
  class VolumeCroppingTool extends BaseTool {
19
19
  constructor(toolProps = {}, defaultToolProps = {
20
+ supportedInteractionTypes: ['Mouse', 'Touch'],
20
21
  configuration: {
21
22
  showCornerSpheres: true,
22
23
  showHandles: true,
@@ -125,8 +126,13 @@ class VolumeCroppingTool extends BaseTool {
125
126
  this._hasResolutionChanged = true;
126
127
  if (this.cleanUp !== null) {
127
128
  document.removeEventListener('mouseup', this.cleanUp);
129
+ document.removeEventListener('touchend', this.cleanUp);
130
+ document.removeEventListener('touchcancel', this.cleanUp);
128
131
  }
129
132
  this.cleanUp = () => {
133
+ document.removeEventListener('mouseup', this.cleanUp);
134
+ document.removeEventListener('touchend', this.cleanUp);
135
+ document.removeEventListener('touchcancel', this.cleanUp);
130
136
  mapper.setSampleDistance(originalSampleDistance);
131
137
  evt.target.style.cursor = '';
132
138
  if (this.draggingSphereIndex !== null) {
@@ -148,6 +154,8 @@ class VolumeCroppingTool extends BaseTool {
148
154
  this._hasResolutionChanged = false;
149
155
  };
150
156
  document.addEventListener('mouseup', this.cleanUp, { once: true });
157
+ document.addEventListener('touchend', this.cleanUp, { once: true });
158
+ document.addEventListener('touchcancel', this.cleanUp, { once: true });
151
159
  }
152
160
  return true;
153
161
  };
@@ -1,10 +1,14 @@
1
1
  import { BaseTool } from './base/index.js';
2
- import type { PublicToolProps, ToolProps } from '../types/index.js';
2
+ import type { EventTypes, PublicToolProps, ToolProps } from '../types/index.js';
3
3
  import type { MouseWheelEventType } from '../types/EventTypes.js';
4
4
  declare class VolumeRotateTool extends BaseTool {
5
5
  static toolName: any;
6
6
  _configuration: unknown;
7
7
  constructor(toolProps?: PublicToolProps, defaultToolProps?: ToolProps);
8
8
  mouseWheelCallback(evt: MouseWheelEventType): void;
9
+ mouseDragCallback(evt: EventTypes.InteractionEventType): void;
10
+ touchDragCallback(evt: EventTypes.InteractionEventType): void;
11
+ _dragCallback(evt: EventTypes.InteractionEventType): void;
12
+ _rotate(element: HTMLDivElement, angle: number): void;
9
13
  }
10
14
  export default VolumeRotateTool;
@@ -15,21 +15,42 @@ class VolumeRotateTool extends BaseTool {
15
15
  configuration: {
16
16
  direction: DIRECTIONS.Z,
17
17
  rotateIncrementDegrees: 30,
18
+ rotateDragDegreesPerPixel: 0.5,
18
19
  },
19
20
  }) {
20
21
  super(toolProps, defaultToolProps);
21
22
  }
22
23
  mouseWheelCallback(evt) {
23
24
  const { element, wheel } = evt.detail;
25
+ const { rotateIncrementDegrees } = this.configuration;
26
+ const { direction: deltaY } = wheel;
27
+ const angle = (deltaY * (rotateIncrementDegrees * Math.PI)) / 180;
28
+ this._rotate(element, angle);
29
+ }
30
+ mouseDragCallback(evt) {
31
+ this._dragCallback(evt);
32
+ }
33
+ touchDragCallback(evt) {
34
+ this._dragCallback(evt);
35
+ }
36
+ _dragCallback(evt) {
37
+ const { element, deltaPoints } = evt.detail;
38
+ const { rotateDragDegreesPerPixel } = this.configuration;
39
+ const deltaX = deltaPoints.canvas[0];
40
+ if (!deltaX) {
41
+ return;
42
+ }
43
+ const angle = (deltaX * rotateDragDegreesPerPixel * Math.PI) / 180;
44
+ this._rotate(element, angle);
45
+ }
46
+ _rotate(element, angle) {
24
47
  const enabledElement = getEnabledElement(element);
25
48
  const { viewport } = enabledElement;
26
- const { direction, rotateIncrementDegrees } = this.configuration;
49
+ const { direction } = this.configuration;
27
50
  const camera = getViewportICamera(viewport);
28
51
  const { viewUp, position, focalPoint } = camera;
29
- const { direction: deltaY } = wheel;
30
52
  const [cx, cy, cz] = focalPoint;
31
53
  const [ax, ay, az] = direction;
32
- const angle = (deltaY * (rotateIncrementDegrees * Math.PI)) / 180;
33
54
  const newPosition = [0, 0, 0];
34
55
  const newFocalPoint = [0, 0, 0];
35
56
  const newViewUp = [0, 0, 0];
@@ -30,7 +30,7 @@ declare class ArrowAnnotateTool extends AnnotationTool {
30
30
  _endCallback: (evt: EventTypes.InteractionEventType) => void;
31
31
  _dragCallback: (evt: EventTypes.InteractionEventType) => void;
32
32
  touchTapCallback: (evt: EventTypes.TouchTapEventType) => void;
33
- doubleClickCallback: (evt: EventTypes.TouchTapEventType) => void;
33
+ doubleClickCallback: (evt: EventTypes.TouchTapEventType, proximity?: number) => void;
34
34
  _doneChangingTextCallback(element: any, annotation: any, updatedLabel: any): void;
35
35
  cancel: (element: HTMLDivElement) => string;
36
36
  _activateModify: (element: HTMLDivElement) => void;
@@ -181,10 +181,10 @@ class ArrowAnnotateTool extends AnnotationTool {
181
181
  };
182
182
  this.touchTapCallback = (evt) => {
183
183
  if (evt.detail.taps == 2) {
184
- this.doubleClickCallback(evt);
184
+ this.doubleClickCallback(evt, _a.TOUCH_PROXIMITY);
185
185
  }
186
186
  };
187
- this.doubleClickCallback = (evt) => {
187
+ this.doubleClickCallback = (evt, proximity = _a.MOUSE_PROXIMITY) => {
188
188
  const eventDetail = evt.detail;
189
189
  const { element } = eventDetail;
190
190
  let annotations = getAnnotations(this.getToolName(), element);
@@ -192,7 +192,7 @@ class ArrowAnnotateTool extends AnnotationTool {
192
192
  if (!annotations?.length) {
193
193
  return;
194
194
  }
195
- const clickedAnnotation = annotations.find((annotation) => this.isPointNearTool(element, annotation, eventDetail.currentPoints.canvas, 6));
195
+ const clickedAnnotation = annotations.find((annotation) => this.isPointNearTool(element, annotation, eventDetail.currentPoints.canvas, proximity));
196
196
  if (!clickedAnnotation) {
197
197
  return;
198
198
  }
@@ -36,6 +36,7 @@ declare class ClickSegmentTool extends GrowCutBaseTool {
36
36
  private runConfinementDryRun;
37
37
  private probeAppliesToClick;
38
38
  preMouseDownCallback(evt: EventTypes.MouseDownActivateEventType): Promise<boolean>;
39
+ preTouchStartCallback: (evt: EventTypes.TouchStartEventType) => Promise<boolean>;
39
40
  private runClick;
40
41
  private fillWithRange;
41
42
  private indexToWorld;
@@ -52,6 +52,20 @@ class ClickSegmentTool extends GrowCutBaseTool {
52
52
  this.dryRunToken = null;
53
53
  this.confirmedRegion = null;
54
54
  this.rejectedRegions = [];
55
+ this.preTouchStartCallback = async (evt) => {
56
+ if (this.mode !== ToolModes.Active || this.segmentationInProgress) {
57
+ return false;
58
+ }
59
+ await this.runHoverProbe(evt);
60
+ if (this.lastProbe && !this.lastProbe.ok) {
61
+ growCutLog.info('tap ignored: probe reported no proper region', {
62
+ canvasPoint: this.lastProbe.canvas,
63
+ });
64
+ this.notifySegmentationError('No meaningful region at the tapped location; nothing was segmented.');
65
+ return true;
66
+ }
67
+ return this.preMouseDownCallback(evt);
68
+ };
55
69
  }
56
70
  notifySegmentationError(message) {
57
71
  const event = new CustomEvent(CoreEnums.Events.ERROR_EVENT, {
@@ -2,6 +2,7 @@ import { getEnabledElement } from '@cornerstonejs/core';
2
2
  import { drawHandles as drawHandlesSvg, drawTextBox as drawTextBoxSvg, } from '../../drawingSvg/index.js';
3
3
  import { getViewportIdsWithToolToRender } from '../../utilities/viewportFilters/index.js';
4
4
  import { hideElementCursor } from '../../cursors/elementCursor.js';
5
+ import { Events } from '../../enums/index.js';
5
6
  import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds.js';
6
7
  import getViewportICamera from '../../utilities/getViewportICamera.js';
7
8
  import ProbeTool from './ProbeTool.js';
@@ -18,9 +19,10 @@ class DragProbeTool extends ProbeTool {
18
19
  this.postMouseDownCallback = (evt) => {
19
20
  const eventDetail = evt.detail;
20
21
  const { currentPoints, element } = eventDetail;
21
- const worldPos = currentPoints.world;
22
22
  const enabledElement = getEnabledElement(element);
23
23
  const { viewport } = enabledElement;
24
+ const isTouch = evt.type === Events.TOUCH_START;
25
+ const worldPos = this.getTouchAdjustedWorldPos(viewport, currentPoints, isTouch);
24
26
  this.isDrawing = true;
25
27
  const camera = getViewportICamera(viewport);
26
28
  const { viewPlaneNormal, viewUp } = camera;
@@ -29,6 +29,7 @@ declare class LivewireContourTool extends ContourSegmentationBaseTool {
29
29
  contourHoleProcessingEnabled?: boolean;
30
30
  } | null;
31
31
  isDrawing: boolean;
32
+ handlesMultiTouchGestures: boolean;
32
33
  isHandleOutsideImage: boolean;
33
34
  constructor(toolProps?: PublicToolProps, defaultToolProps?: ToolProps);
34
35
  protected setupBaseEditData(worldPos: any, element: any, annotation: any, nextPos?: any, contourHoleProcessingEnabled?: any): void;
@@ -41,6 +42,8 @@ declare class LivewireContourTool extends ContourSegmentationBaseTool {
41
42
  triggerChangeEvent: (annotation: LivewireContourAnnotation, enabledElement: Types.IEnabledElement, changeType?: ChangeTypes, contourHoleProcessingEnabled?: boolean) => void;
42
43
  private _mouseDownCallback;
43
44
  private _mouseMoveCallback;
45
+ private _touchDragDuringDrawCallback;
46
+ private _touchEndDuringDrawCallback;
44
47
  editHandle(worldPos: Types.Point3, element: any, annotation: LivewireContourAnnotation, handleIndex: number): void;
45
48
  protected _dragCallback: (evt: EventTypes.InteractionEventType) => void;
46
49
  cancel: (element: HTMLDivElement) => string;
@@ -25,6 +25,7 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
25
25
  preventHandleOutsideImage: false,
26
26
  contourHoleAdditionModifierKey: KeyboardBindings.Shift,
27
27
  snapHandleNearby: 2,
28
+ touchCloseCurveDistance: 36,
28
29
  interpolation: {
29
30
  enabled: false,
30
31
  nearestEdge: 2,
@@ -47,6 +48,7 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
47
48
  },
48
49
  }) {
49
50
  super(toolProps, defaultToolProps);
51
+ this.handlesMultiTouchGestures = true;
50
52
  this.isHandleOutsideImage = false;
51
53
  this.isPointNearTool = (element, annotation, canvasCoords, proximity) => {
52
54
  const enabledElement = getEnabledElement(element);
@@ -137,7 +139,14 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
137
139
  }
138
140
  };
139
141
  this._mouseDownCallback = (evt) => {
140
- const doubleClick = evt.type === Events.MOUSE_DOUBLE_CLICK;
142
+ const isTouchEvent = evt.type === Events.TOUCH_TAP || evt.type === Events.TOUCH_END;
143
+ const doubleTap = evt.type === Events.TOUCH_TAP &&
144
+ evt.detail.taps >= 2;
145
+ const doubleClick = evt.type === Events.MOUSE_DOUBLE_CLICK || doubleTap;
146
+ if (doubleTap &&
147
+ this.isTouchTapEchoOfLiftCommit(evt.detail.currentPoints.canvas)) {
148
+ return;
149
+ }
141
150
  const { annotation, viewportIdsToRender, worldToSlice, sliceToWorld, newAnnotation, } = this.editData;
142
151
  if (this.editData.closed) {
143
152
  return;
@@ -160,12 +169,15 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
160
169
  index: -1,
161
170
  distSquared: Infinity,
162
171
  };
172
+ const closeDistSquared = isTouchEvent
173
+ ? this.configuration.touchCloseCurveDistance ** 2
174
+ : CLICK_CLOSE_CURVE_SQR_DIST;
163
175
  for (let i = 0, len = controlPoints.length; i < len; i++) {
164
176
  const controlPoint = controlPoints[i];
165
177
  const worldControlPoint = sliceToWorld(controlPoint);
166
178
  const canvasControlPoint = viewport.worldToCanvas(worldControlPoint);
167
179
  const distSquared = math.point.distanceToPointSquared(canvasPos, canvasControlPoint);
168
- if (distSquared <= CLICK_CLOSE_CURVE_SQR_DIST &&
180
+ if (distSquared <= closeDistSquared &&
169
181
  distSquared < closestHandlePoint.distSquared) {
170
182
  closestHandlePoint.distSquared = distSquared;
171
183
  closestHandlePoint.index = i;
@@ -224,6 +236,24 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
224
236
  triggerAnnotationRenderForViewportIds(viewportIdsToRender);
225
237
  evt.preventDefault();
226
238
  };
239
+ this._touchDragDuringDrawCallback = (evt) => {
240
+ if (evt.detail.currentPointsList.length > 1) {
241
+ return;
242
+ }
243
+ this._mouseMoveCallback(evt);
244
+ };
245
+ this._touchEndDuringDrawCallback = (evt) => {
246
+ const { startPointsList, currentPointsList, startPoints, currentPoints } = evt.detail;
247
+ if (startPointsList.length > 1 || currentPointsList.length > 1) {
248
+ return;
249
+ }
250
+ const dragDistance = math.point.distanceToPoint(startPoints.canvas, currentPoints.canvas);
251
+ if (dragDistance <= LivewireContourTool.TOUCH_TAP_MAX_CANVAS_DISTANCE) {
252
+ return;
253
+ }
254
+ this.recordTouchLiftCommit(currentPoints.canvas);
255
+ this._mouseDownCallback(evt);
256
+ };
227
257
  this._dragCallback = (evt) => {
228
258
  this.isDrawing = true;
229
259
  const eventDetail = evt.detail;
@@ -274,6 +304,8 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
274
304
  element.addEventListener(Events.MOUSE_DOWN, this._mouseDownCallback);
275
305
  element.addEventListener(Events.MOUSE_DOUBLE_CLICK, this._mouseDownCallback);
276
306
  element.addEventListener(Events.TOUCH_TAP, this._mouseDownCallback);
307
+ element.addEventListener(Events.TOUCH_DRAG, this._touchDragDuringDrawCallback);
308
+ element.addEventListener(Events.TOUCH_END, this._touchEndDuringDrawCallback);
277
309
  };
278
310
  this._deactivateDraw = (element) => {
279
311
  state.isInteractingWithTool = false;
@@ -281,6 +313,8 @@ class LivewireContourTool extends ContourSegmentationBaseTool {
281
313
  element.removeEventListener(Events.MOUSE_DOWN, this._mouseDownCallback);
282
314
  element.removeEventListener(Events.MOUSE_DOUBLE_CLICK, this._mouseDownCallback);
283
315
  element.removeEventListener(Events.TOUCH_TAP, this._mouseDownCallback);
316
+ element.removeEventListener(Events.TOUCH_DRAG, this._touchDragDuringDrawCallback);
317
+ element.removeEventListener(Events.TOUCH_END, this._touchEndDuringDrawCallback);
284
318
  };
285
319
  this._calculateCachedStats = (annotation, element) => {
286
320
  if (!this.configuration.calculateStats) {
@@ -1,7 +1,7 @@
1
1
  import type { Types } from '@cornerstonejs/core';
2
2
  import { AnnotationTool } from '../base/index.js';
3
3
  import { ChangeTypes } from '../../enums/index.js';
4
- import type { EventTypes, ToolHandle, PublicToolProps, SVGDrawingHelper } from '../../types/index.js';
4
+ import type { EventTypes, ToolHandle, PublicToolProps, SVGDrawingHelper, InteractionTypes } from '../../types/index.js';
5
5
  import type { ProbeAnnotation } from '../../types/ToolSpecificAnnotationTypes.js';
6
6
  declare class ProbeTool extends AnnotationTool {
7
7
  static toolName: string;
@@ -16,6 +16,10 @@ declare class ProbeTool extends AnnotationTool {
16
16
  x: number;
17
17
  y: number;
18
18
  };
19
+ touchCanvasOffset: {
20
+ x: number;
21
+ y: number;
22
+ };
19
23
  };
20
24
  };
21
25
  constructor(toolProps?: PublicToolProps, defaultToolProps?: any);
@@ -28,8 +32,12 @@ declare class ProbeTool extends AnnotationTool {
28
32
  viewplaneNormal?: Types.Point3;
29
33
  viewUp?: Types.Point3;
30
34
  }) => ProbeAnnotation;
31
- addNewAnnotation: (evt: EventTypes.InteractionEventType) => ProbeAnnotation;
35
+ addNewAnnotation: (evt: EventTypes.InteractionEventType, interactionType?: InteractionTypes) => ProbeAnnotation;
32
36
  getHandleNearImagePoint(element: HTMLDivElement, annotation: ProbeAnnotation, canvasCoords: Types.Point2, proximity: number): ToolHandle | undefined;
37
+ protected getTouchAdjustedWorldPos(viewport: Types.IViewport, currentPoints: {
38
+ canvas: Types.Point2;
39
+ world: Types.Point3;
40
+ }, isTouch: boolean): Types.Point3;
33
41
  handleSelectedCallback(evt: EventTypes.InteractionEventType, annotation: ProbeAnnotation): void;
34
42
  _endCallback: (evt: EventTypes.InteractionEventType) => void;
35
43
  _dragCallback: (evt: any) => void;
@@ -20,12 +20,13 @@ const { transformWorldToIndex } = csUtils;
20
20
  class ProbeTool extends AnnotationTool {
21
21
  constructor(toolProps = {}, defaultToolProps) {
22
22
  super(toolProps, AnnotationTool.mergeDefaultProps(_a.probeDefaults, defaultToolProps));
23
- this.addNewAnnotation = (evt) => {
23
+ this.addNewAnnotation = (evt, interactionType = 'Mouse') => {
24
24
  const eventDetail = evt.detail;
25
25
  const { currentPoints, element } = eventDetail;
26
- const worldPos = currentPoints.world;
27
26
  const enabledElement = getEnabledElement(element);
28
27
  const { viewport } = enabledElement;
28
+ const isTouch = interactionType.toLowerCase() === 'touch';
29
+ const worldPos = this.getTouchAdjustedWorldPos(viewport, currentPoints, isTouch);
29
30
  this.isDrawing = true;
30
31
  const annotation = (this.constructor).createAnnotationForViewport(viewport, {
31
32
  data: {
@@ -75,7 +76,9 @@ class ProbeTool extends AnnotationTool {
75
76
  this.isDrawing = true;
76
77
  const eventDetail = evt.detail;
77
78
  const { currentPoints, element } = eventDetail;
78
- const worldPos = currentPoints.world;
79
+ const { viewport } = getEnabledElement(element);
80
+ const isTouch = evt.type === Events.TOUCH_DRAG;
81
+ const worldPos = this.getTouchAdjustedWorldPos(viewport, currentPoints, isTouch);
79
82
  const { annotation, viewportIdsToRender, newAnnotation } = this.editData;
80
83
  const { data } = annotation;
81
84
  this.createMemo(element, annotation, { newAnnotation });
@@ -231,6 +234,18 @@ class ProbeTool extends AnnotationTool {
231
234
  return point;
232
235
  }
233
236
  }
237
+ getTouchAdjustedWorldPos(viewport, currentPoints, isTouch) {
238
+ const { touchCanvasOffset } = this.configuration;
239
+ if (!isTouch || !touchCanvasOffset) {
240
+ return currentPoints.world;
241
+ }
242
+ const { x = 0, y = 0 } = touchCanvasOffset;
243
+ if (!x && !y) {
244
+ return currentPoints.world;
245
+ }
246
+ const [canvasX, canvasY] = currentPoints.canvas;
247
+ return viewport.canvasToWorld([canvasX + x, canvasY + y]);
248
+ }
234
249
  handleSelectedCallback(evt, annotation) {
235
250
  const eventDetail = evt.detail;
236
251
  const { element } = eventDetail;
@@ -336,6 +351,10 @@ ProbeTool.probeDefaults = {
336
351
  x: 6,
337
352
  y: -6,
338
353
  },
354
+ touchCanvasOffset: {
355
+ x: 0,
356
+ y: -40,
357
+ },
339
358
  },
340
359
  };
341
360
  ProbeTool.hydrate = (viewportId, points, options) => {
@@ -40,6 +40,9 @@ class RegionSegmentTool extends GrowCutBaseTool {
40
40
  element.removeEventListener(Events.MOUSE_UP, this._endCallback);
41
41
  element.removeEventListener(Events.MOUSE_DRAG, this._dragCallback);
42
42
  element.removeEventListener(Events.MOUSE_CLICK, this._endCallback);
43
+ element.removeEventListener(Events.TOUCH_END, this._endCallback);
44
+ element.removeEventListener(Events.TOUCH_DRAG, this._dragCallback);
45
+ element.removeEventListener(Events.TOUCH_TAP, this._endCallback);
43
46
  };
44
47
  }
45
48
  async preMouseDownCallback(evt) {
@@ -84,6 +87,9 @@ class RegionSegmentTool extends GrowCutBaseTool {
84
87
  element.addEventListener(Events.MOUSE_UP, this._endCallback);
85
88
  element.addEventListener(Events.MOUSE_DRAG, this._dragCallback);
86
89
  element.addEventListener(Events.MOUSE_CLICK, this._endCallback);
90
+ element.addEventListener(Events.TOUCH_END, this._endCallback);
91
+ element.addEventListener(Events.TOUCH_DRAG, this._dragCallback);
92
+ element.addEventListener(Events.TOUCH_TAP, this._endCallback);
87
93
  }
88
94
  renderAnnotation(enabledElement, svgDrawingHelper) {
89
95
  if (!this.growCutData) {
@@ -28,9 +28,11 @@ declare class SplineROITool extends ContourSegmentationBaseTool {
28
28
  newAnnotation?: boolean;
29
29
  hasMoved?: boolean;
30
30
  lastCanvasPoint?: Types.Point2;
31
+ closeCurveDistance?: number;
31
32
  contourHoleProcessingEnabled?: boolean;
32
33
  } | null;
33
34
  isDrawing: boolean;
35
+ handlesMultiTouchGestures: boolean;
34
36
  isHandleOutsideImage: boolean;
35
37
  fireChangeOnUpdate: {
36
38
  annotationUID: string;
@@ -52,6 +54,8 @@ declare class SplineROITool extends ContourSegmentationBaseTool {
52
54
  private _keyDownCallback;
53
55
  private _mouseMoveCallback;
54
56
  private _mouseDownCallback;
57
+ private _touchDragDuringDrawCallback;
58
+ private _touchEndDuringDrawCallback;
55
59
  protected _dragCallback: (evt: EventTypes.InteractionEventType) => void;
56
60
  cancel(element: HTMLDivElement): string;
57
61
  triggerAnnotationCompleted: (annotation: SplineROIAnnotation, contourHoleProcessingEnabled: boolean) => void;