@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
@@ -82,6 +82,7 @@ class SplineROITool extends ContourSegmentationBaseTool {
82
82
  type: SplineTypesEnum.CatmullRom,
83
83
  drawPreviewEnabled: true,
84
84
  enableTwoPointPreview: false,
85
+ touchCloseCurveDistance: 36,
85
86
  lastControlPointDeletionKeys: ['Backspace', 'Delete'],
86
87
  },
87
88
  actions: {
@@ -113,6 +114,7 @@ class SplineROITool extends ContourSegmentationBaseTool {
113
114
  'BSplineROI',
114
115
  'CardinalSplineROI',
115
116
  ];
117
+ this.handlesMultiTouchGestures = true;
116
118
  this.isHandleOutsideImage = false;
117
119
  this.fireChangeOnUpdate = null;
118
120
  this.isPointNearTool = (element, annotation, canvasCoords, proximity) => {
@@ -229,11 +231,22 @@ class SplineROITool extends ContourSegmentationBaseTool {
229
231
  const { renderingEngine } = getEnabledElement(element);
230
232
  const viewportIdsToRender = getViewportIdsWithToolToRender(element, this.getToolName());
231
233
  this.editData.lastCanvasPoint = evt.detail.currentPoints.canvas;
234
+ this.editData.closeCurveDistance =
235
+ evt.type === Events.TOUCH_DRAG
236
+ ? this.configuration.spline.touchCloseCurveDistance
237
+ : SPLINE_CLICK_CLOSE_CURVE_DIST;
232
238
  triggerAnnotationRenderForViewportIds(viewportIdsToRender);
233
239
  evt.preventDefault();
234
240
  };
235
241
  this._mouseDownCallback = (evt) => {
236
- const doubleClick = evt.type === Events.MOUSE_DOUBLE_CLICK;
242
+ const isTouchEvent = evt.type === Events.TOUCH_TAP || evt.type === Events.TOUCH_END;
243
+ const doubleTap = evt.type === Events.TOUCH_TAP &&
244
+ evt.detail.taps >= 2;
245
+ const doubleClick = evt.type === Events.MOUSE_DOUBLE_CLICK || doubleTap;
246
+ if (doubleTap &&
247
+ this.isTouchTapEchoOfLiftCommit(evt.detail.currentPoints.canvas)) {
248
+ return;
249
+ }
237
250
  const { annotation, viewportIdsToRender } = this.editData;
238
251
  const { data } = annotation;
239
252
  if (data.contour.closed) {
@@ -255,7 +268,10 @@ class SplineROITool extends ContourSegmentationBaseTool {
255
268
  if (data.handles.points.length >= 3) {
256
269
  this.createMemo(element, annotation);
257
270
  const { instance: spline } = data.spline;
258
- const closestControlPoint = spline.getClosestControlPointWithinDistance(canvasPoint, SPLINE_CLICK_CLOSE_CURVE_DIST);
271
+ const closeCurveDistance = isTouchEvent
272
+ ? this.configuration.spline.touchCloseCurveDistance
273
+ : SPLINE_CLICK_CLOSE_CURVE_DIST;
274
+ const closestControlPoint = spline.getClosestControlPointWithinDistance(canvasPoint, closeCurveDistance);
259
275
  if (closestControlPoint?.index === 0) {
260
276
  addNewPoint = false;
261
277
  closeContour = !this.configuration.allowOpenSplines;
@@ -275,6 +291,24 @@ class SplineROITool extends ContourSegmentationBaseTool {
275
291
  }
276
292
  evt.preventDefault();
277
293
  };
294
+ this._touchDragDuringDrawCallback = (evt) => {
295
+ if (evt.detail.currentPointsList.length > 1) {
296
+ return;
297
+ }
298
+ this._mouseMoveCallback(evt);
299
+ };
300
+ this._touchEndDuringDrawCallback = (evt) => {
301
+ const { startPointsList, currentPointsList, startPoints, currentPoints } = evt.detail;
302
+ if (startPointsList.length > 1 || currentPointsList.length > 1) {
303
+ return;
304
+ }
305
+ const dragDistance = math.point.distanceToPoint(startPoints.canvas, currentPoints.canvas);
306
+ if (dragDistance <= _a.TOUCH_TAP_MAX_CANVAS_DISTANCE) {
307
+ return;
308
+ }
309
+ this.recordTouchLiftCommit(currentPoints.canvas);
310
+ this._mouseDownCallback(evt);
311
+ };
278
312
  this._dragCallback = (evt) => {
279
313
  this.isDrawing = true;
280
314
  const eventDetail = evt.detail;
@@ -343,6 +377,8 @@ class SplineROITool extends ContourSegmentationBaseTool {
343
377
  element.addEventListener(Events.MOUSE_DOWN, this._mouseDownCallback);
344
378
  element.addEventListener(Events.MOUSE_DOUBLE_CLICK, this._mouseDownCallback);
345
379
  element.addEventListener(Events.TOUCH_TAP, this._mouseDownCallback);
380
+ element.addEventListener(Events.TOUCH_DRAG, this._touchDragDuringDrawCallback);
381
+ element.addEventListener(Events.TOUCH_END, this._touchEndDuringDrawCallback);
346
382
  };
347
383
  this._deactivateDraw = (element) => {
348
384
  state.isInteractingWithTool = false;
@@ -351,6 +387,8 @@ class SplineROITool extends ContourSegmentationBaseTool {
351
387
  element.removeEventListener(Events.MOUSE_DOWN, this._mouseDownCallback);
352
388
  element.removeEventListener(Events.MOUSE_DOUBLE_CLICK, this._mouseDownCallback);
353
389
  element.removeEventListener(Events.TOUCH_TAP, this._mouseDownCallback);
390
+ element.removeEventListener(Events.TOUCH_DRAG, this._touchDragDuringDrawCallback);
391
+ element.removeEventListener(Events.TOUCH_END, this._touchEndDuringDrawCallback);
354
392
  };
355
393
  this._renderStats = (annotation, enabledElement, svgDrawingHelper) => {
356
394
  const data = annotation.data;
@@ -631,7 +669,7 @@ class SplineROITool extends ContourSegmentationBaseTool {
631
669
  });
632
670
  }
633
671
  else if (spline.numControlPoints > 1) {
634
- const previewPolylinePoints = spline.getPreviewPolylinePoints(lastCanvasPoint, SPLINE_CLICK_CLOSE_CURVE_DIST);
672
+ const previewPolylinePoints = spline.getPreviewPolylinePoints(lastCanvasPoint, this.editData?.closeCurveDistance ?? SPLINE_CLICK_CLOSE_CURVE_DIST);
635
673
  drawPolylineSvg(svgDrawingHelper, annotationUID, 'previewSplineChange', previewPolylinePoints, {
636
674
  color: '#9EA0CA',
637
675
  lineDash: lineDash,
@@ -40,8 +40,8 @@ class WholeBodySegmentTool extends GrowCutBaseTool {
40
40
  const { element } = eventData;
41
41
  const enabledElement = getEnabledElement(element);
42
42
  const { viewport } = enabledElement;
43
- await this.runGrowCut();
44
43
  this._deactivateDraw(element);
44
+ await this.runGrowCut();
45
45
  this.growCutData = null;
46
46
  resetElementCursor(element);
47
47
  triggerAnnotationRenderForViewportUIDs([viewport.id]);
@@ -50,6 +50,9 @@ class WholeBodySegmentTool extends GrowCutBaseTool {
50
50
  element.removeEventListener(Events.MOUSE_UP, this._endCallback);
51
51
  element.removeEventListener(Events.MOUSE_DRAG, this._dragCallback);
52
52
  element.removeEventListener(Events.MOUSE_CLICK, this._endCallback);
53
+ element.removeEventListener(Events.TOUCH_END, this._endCallback);
54
+ element.removeEventListener(Events.TOUCH_DRAG, this._dragCallback);
55
+ element.removeEventListener(Events.TOUCH_TAP, this._endCallback);
53
56
  };
54
57
  }
55
58
  async preMouseDownCallback(evt) {
@@ -152,6 +155,9 @@ class WholeBodySegmentTool extends GrowCutBaseTool {
152
155
  element.addEventListener(Events.MOUSE_UP, this._endCallback);
153
156
  element.addEventListener(Events.MOUSE_DRAG, this._dragCallback);
154
157
  element.addEventListener(Events.MOUSE_CLICK, this._endCallback);
158
+ element.addEventListener(Events.TOUCH_END, this._endCallback);
159
+ element.addEventListener(Events.TOUCH_DRAG, this._dragCallback);
160
+ element.addEventListener(Events.TOUCH_TAP, this._endCallback);
155
161
  }
156
162
  _projectWorldPointAcrossSlices(viewport, worldEdgePoint, vecDirection) {
157
163
  const volume = this._getViewportVolume(viewport);
@@ -2,11 +2,16 @@ import { utilities as csUtils } from '@cornerstonejs/core';
2
2
  import type { Types } from '@cornerstonejs/core';
3
3
  import ToolModes from '../../enums/ToolModes.js';
4
4
  import type StrategyCallbacks from '../../enums/StrategyCallbacks.js';
5
- import type { InteractionTypes, ToolProps, PublicToolProps, ToolConfiguration, AnnotationData, MeasurementTargetCandidate } from '../../types/index.js';
5
+ import type { EventTypes, InteractionTypes, ToolProps, PublicToolProps, ToolConfiguration, AnnotationData, MeasurementTargetCandidate } from '../../types/index.js';
6
6
  declare abstract class BaseTool {
7
7
  static toolName: any;
8
8
  static activeCursorTool: any;
9
+ static readonly TOUCH_PROXIMITY = 36;
10
+ static readonly MOUSE_PROXIMITY = 6;
11
+ static readonly TOUCH_TAP_MAX_CANVAS_DISTANCE = 24;
12
+ static readonly TOUCH_TAP_TOLERANCE_MS = 300;
9
13
  supportedInteractionTypes: InteractionTypes[];
14
+ handlesMultiTouchGestures: boolean;
10
15
  configuration: Record<string, any>;
11
16
  get configurationTyped(): ToolConfiguration;
12
17
  toolGroupId: string;
@@ -32,6 +37,7 @@ declare abstract class BaseTool {
32
37
  applyActiveStrategyCallback(enabledElement: Types.IEnabledElement, operationData: unknown, callbackType: StrategyCallbacks | string, ...extraArgs: any[]): any;
33
38
  setConfiguration(newConfiguration: Record<string, any>): void;
34
39
  setActiveStrategy(strategyName: string): void;
40
+ protected _isTouchInteraction(evt?: EventTypes.InteractionEventType): boolean;
35
41
  protected getTargetImageData(targetId: string): Types.IImageData | Types.CPUIImageData;
36
42
  protected getTargetId(viewport: Types.IViewport, data?: AnnotationData): string | undefined;
37
43
  protected getMeasurementTargets(viewport: Types.IViewport, data?: AnnotationData): string[];
@@ -1,5 +1,7 @@
1
1
  import { cache, metaData, utilities as csUtils, viewportHasPan, viewportHasZoom, } from '@cornerstonejs/core';
2
2
  import ToolModes from '../../enums/ToolModes.js';
3
+ import Events from '../../enums/Events.js';
4
+ import { MOUSE_PROXIMITY, TOUCH_PROXIMITY, TOUCH_TAP_MAX_CANVAS_DISTANCE, TOUCH_TAP_TOLERANCE_MS, } from '../../utilities/touch/constants.js';
3
5
  import measurementTargetFilters from './measurementTargetFilters.js';
4
6
  const { DefaultHistoryMemo } = csUtils.HistoryMemo;
5
7
  class BaseTool {
@@ -7,6 +9,7 @@ class BaseTool {
7
9
  return this.configuration;
8
10
  }
9
11
  constructor(toolProps, defaultToolProps) {
12
+ this.handlesMultiTouchGestures = false;
10
13
  this.isPrimary = false;
11
14
  const mergedDefaults = BaseTool.mergeDefaultProps(BaseTool.defaults, defaultToolProps);
12
15
  const initialProps = csUtils.deepMerge(mergedDefaults, toolProps);
@@ -50,6 +53,15 @@ class BaseTool {
50
53
  setActiveStrategy(strategyName) {
51
54
  this.setConfiguration({ activeStrategy: strategyName });
52
55
  }
56
+ _isTouchInteraction(evt) {
57
+ const eventName = evt?.detail?.eventName;
58
+ return (eventName === Events.TOUCH_START ||
59
+ eventName === Events.TOUCH_START_ACTIVATE ||
60
+ eventName === Events.TOUCH_DRAG ||
61
+ eventName === Events.TOUCH_END ||
62
+ eventName === Events.TOUCH_TAP ||
63
+ eventName === Events.TOUCH_PRESS);
64
+ }
53
65
  getTargetImageData(targetId) {
54
66
  if (targetId.startsWith('imageId:')) {
55
67
  const imageId = targetId.split('imageId:')[1];
@@ -293,6 +305,10 @@ class BaseTool {
293
305
  return true;
294
306
  }
295
307
  }
308
+ BaseTool.TOUCH_PROXIMITY = TOUCH_PROXIMITY;
309
+ BaseTool.MOUSE_PROXIMITY = MOUSE_PROXIMITY;
310
+ BaseTool.TOUCH_TAP_MAX_CANVAS_DISTANCE = TOUCH_TAP_MAX_CANVAS_DISTANCE;
311
+ BaseTool.TOUCH_TAP_TOLERANCE_MS = TOUCH_TAP_TOLERANCE_MS;
296
312
  BaseTool.defaults = {
297
313
  configuration: {
298
314
  strategies: {},
@@ -1,10 +1,14 @@
1
+ import type { Types } from '@cornerstonejs/core';
1
2
  import type { Annotation, EventTypes, PublicToolProps, ToolProps, AnnotationRenderContext, Annotations } from '../../types/index.js';
2
3
  import type { ContourAnnotation } from '../../types/ToolSpecificAnnotationTypes.js';
3
4
  import type { StyleSpecifier } from '../../types/AnnotationStyle.js';
4
5
  import ContourBaseTool from './ContourBaseTool.js';
5
6
  declare abstract class ContourSegmentationBaseTool extends ContourBaseTool {
6
7
  static PreviewSegmentIndex: number;
8
+ private _touchLiftCommit?;
7
9
  constructor(toolProps: PublicToolProps, defaultToolProps: ToolProps);
10
+ protected recordTouchLiftCommit(canvasPoint: Types.Point2): void;
11
+ protected isTouchTapEchoOfLiftCommit(canvasPoint: Types.Point2): boolean;
8
12
  protected onSetToolConfiguration(): void;
9
13
  protected isContourSegmentationTool(): boolean;
10
14
  protected createAnnotation(evt: EventTypes.InteractionEventType): ContourAnnotation;
@@ -1,4 +1,5 @@
1
1
  import { getEnabledElement, utilities } from '@cornerstonejs/core';
2
+ import { distanceToPoint } from '../../utilities/math/point/index.js';
2
3
  import { SegmentationRepresentations } from '../../enums/index.js';
3
4
  import ContourBaseTool from './ContourBaseTool.js';
4
5
  import { triggerSegmentationDataModified } from '../../stateManagement/segmentation/triggerSegmentationEvents.js';
@@ -20,6 +21,28 @@ class ContourSegmentationBaseTool extends ContourBaseTool {
20
21
  InterpolationManager.addTool(this.getToolName());
21
22
  }
22
23
  }
24
+ recordTouchLiftCommit(canvasPoint) {
25
+ this._touchLiftCommit = { canvasPoint, time: Date.now() };
26
+ }
27
+ isTouchTapEchoOfLiftCommit(canvasPoint) {
28
+ if (!this._touchLiftCommit) {
29
+ return false;
30
+ }
31
+ const { canvasPoint: committedPoint, time } = this._touchLiftCommit;
32
+ const withinTime = Date.now() - time <=
33
+ 2 * ContourSegmentationBaseTool.TOUCH_TAP_TOLERANCE_MS;
34
+ if (!withinTime) {
35
+ this._touchLiftCommit = undefined;
36
+ return false;
37
+ }
38
+ const withinDistance = distanceToPoint(canvasPoint, committedPoint) <=
39
+ ContourSegmentationBaseTool.TOUCH_TAP_MAX_CANVAS_DISTANCE;
40
+ if (!withinDistance) {
41
+ return false;
42
+ }
43
+ this._touchLiftCommit = undefined;
44
+ return true;
45
+ }
23
46
  onSetToolConfiguration() {
24
47
  if (this.configuration.interpolation?.enabled) {
25
48
  InterpolationManager.addTool(this.getToolName());
@@ -4,6 +4,7 @@ import LabelmapBaseTool from './LabelmapBaseTool.js';
4
4
  declare class BrushTool extends LabelmapBaseTool {
5
5
  static toolName: any;
6
6
  private _lastDragInfo;
7
+ private _drawStartTimeStamp;
7
8
  private _lazyEdit;
8
9
  constructor(toolProps?: PublicToolProps, defaultToolProps?: ToolProps);
9
10
  onSetToolPassive: (_evt: any) => void;
@@ -20,7 +21,9 @@ declare class BrushTool extends LabelmapBaseTool {
20
21
  mouseMoveCallback: (evt: EventTypes.InteractionEventType) => void;
21
22
  previewCallback: () => void;
22
23
  protected updateCursor(evt: EventTypes.InteractionEventType): void;
24
+ private _clearCursor;
23
25
  private _dragCallback;
26
+ protected _cancelTouchDraw(element: HTMLDivElement): void;
24
27
  private _calculateCursor;
25
28
  private _endCallback;
26
29
  getStatistics(element: any, segmentIndices?: any): any;
@@ -83,6 +83,7 @@ class BrushTool extends LabelmapBaseTool {
83
83
  }) {
84
84
  super(toolProps, defaultToolProps);
85
85
  this._lastDragInfo = null;
86
+ this._drawStartTimeStamp = 0;
86
87
  this._lazyEdit = new LazyBrushEditController();
87
88
  this.onSetToolPassive = (_evt) => {
88
89
  this.disableCursor();
@@ -113,6 +114,7 @@ class BrushTool extends LabelmapBaseTool {
113
114
  this._editData = this.createEditData(element);
114
115
  this._previewData.isDrag = false;
115
116
  this._previewData.timerStart = Date.now();
117
+ this._drawStartTimeStamp = eventData.event?.timeStamp ?? 0;
116
118
  const canvasPoint = vec2.clone(currentPoints.canvas);
117
119
  const worldPoint = viewport.canvasToWorld([
118
120
  canvasPoint[0],
@@ -128,6 +130,7 @@ class BrushTool extends LabelmapBaseTool {
128
130
  return false;
129
131
  }
130
132
  this._calculateCursor(element, canvasPoint);
133
+ BrushTool.activeCursorTool = this;
131
134
  this._resetLazyEditState();
132
135
  if (this._isLazyLabelmapEditingEnabled(this._hoverData.viewport)) {
133
136
  this._lazyEdit.appendStrokePoint(worldPoint);
@@ -217,6 +220,11 @@ class BrushTool extends LabelmapBaseTool {
217
220
  this._dragCallback = (evt) => {
218
221
  const eventData = evt.detail;
219
222
  const { element, currentPoints } = eventData;
223
+ const { currentPointsList } = eventData;
224
+ if (currentPointsList?.length > 1) {
225
+ this._cancelTouchDraw(element);
226
+ return;
227
+ }
220
228
  const enabledElement = getEnabledElement(element);
221
229
  const { viewport } = enabledElement;
222
230
  this.updateCursor(evt);
@@ -286,6 +294,12 @@ class BrushTool extends LabelmapBaseTool {
286
294
  this._endCallback = (evt) => {
287
295
  const eventData = evt.detail;
288
296
  const { element } = eventData;
297
+ if (eventData.eventName === Events.TOUCH_TAP) {
298
+ const tapTimeStamp = eventData.event?.timeStamp ?? 0;
299
+ if (tapTimeStamp < this._drawStartTimeStamp) {
300
+ return;
301
+ }
302
+ }
289
303
  const enabledElement = getEnabledElement(element);
290
304
  const operationData = this.getOperationData(element);
291
305
  if (!operationData) {
@@ -307,12 +321,17 @@ class BrushTool extends LabelmapBaseTool {
307
321
  this._editData = null;
308
322
  this._lastDragInfo = null;
309
323
  if (isLazyLabelmapEditing && this._previewData.isDrag) {
310
- this._scheduleLazyPreviewCleanup(element, evt.detail.currentPoints.canvas, enabledElement.viewport.id, operationData.segmentationId);
324
+ this._scheduleLazyPreviewCleanup(element, evt.detail.currentPoints.canvas, enabledElement.viewport.id, operationData.segmentationId, this._isTouchInteraction(evt));
311
325
  triggerAnnotationRenderForViewportUIDs(this._hoverData.viewportIdsToRender);
312
326
  }
313
327
  else {
314
328
  this._resetLazyEditState();
315
- this.updateCursor(evt);
329
+ if (this._isTouchInteraction(evt)) {
330
+ this._clearCursor(element);
331
+ }
332
+ else {
333
+ this.updateCursor(evt);
334
+ }
316
335
  }
317
336
  this.applyActiveStrategyCallback(enabledElement, operationData, StrategyCallbacks.OnInteractionEnd);
318
337
  if (!this._previewData.isDrag) {
@@ -323,11 +342,17 @@ class BrushTool extends LabelmapBaseTool {
323
342
  element.addEventListener(Events.MOUSE_UP, this._endCallback);
324
343
  element.addEventListener(Events.MOUSE_DRAG, this._dragCallback);
325
344
  element.addEventListener(Events.MOUSE_CLICK, this._endCallback);
345
+ element.addEventListener(Events.TOUCH_END, this._endCallback);
346
+ element.addEventListener(Events.TOUCH_DRAG, this._dragCallback);
347
+ element.addEventListener(Events.TOUCH_TAP, this._endCallback);
326
348
  };
327
349
  this._deactivateDraw = (element) => {
328
350
  element.removeEventListener(Events.MOUSE_UP, this._endCallback);
329
351
  element.removeEventListener(Events.MOUSE_DRAG, this._dragCallback);
330
352
  element.removeEventListener(Events.MOUSE_CLICK, this._endCallback);
353
+ element.removeEventListener(Events.TOUCH_END, this._endCallback);
354
+ element.removeEventListener(Events.TOUCH_DRAG, this._dragCallback);
355
+ element.removeEventListener(Events.TOUCH_TAP, this._endCallback);
331
356
  };
332
357
  }
333
358
  disableCursor() {
@@ -354,13 +379,15 @@ class BrushTool extends LabelmapBaseTool {
354
379
  BrushTool.activeCursorTool = this;
355
380
  triggerAnnotationRenderForViewportUIDs(this._hoverData.viewportIdsToRender);
356
381
  }
357
- _scheduleLazyPreviewCleanup(element, centerCanvas, viewportId, segmentationId) {
382
+ _scheduleLazyPreviewCleanup(element, centerCanvas, viewportId, segmentationId, isTouch = false) {
358
383
  this._lazyEdit.scheduleCleanup({
359
384
  element,
360
385
  centerCanvas,
361
386
  viewportId,
362
387
  segmentationId,
363
- refreshCursor: this._refreshCursor.bind(this),
388
+ refreshCursor: isTouch
389
+ ? (cleanupElement) => this._clearCursor(cleanupElement)
390
+ : this._refreshCursor.bind(this),
364
391
  });
365
392
  }
366
393
  _captureLazyPreviewCircle() {
@@ -376,6 +403,37 @@ class BrushTool extends LabelmapBaseTool {
376
403
  const centerCanvas = currentPoints.canvas;
377
404
  this._refreshCursor(element, centerCanvas);
378
405
  }
406
+ _clearCursor(element) {
407
+ const viewportIdsToRender = this._hoverData?.viewportIdsToRender;
408
+ this._hoverData = undefined;
409
+ if (viewportIdsToRender?.length) {
410
+ triggerAnnotationRenderForViewportUIDs(viewportIdsToRender);
411
+ return;
412
+ }
413
+ const enabledElement = getEnabledElement(element);
414
+ if (enabledElement) {
415
+ triggerAnnotationRenderForViewportUIDs([enabledElement.viewport.id]);
416
+ }
417
+ }
418
+ _cancelTouchDraw(element) {
419
+ const { memo } = this;
420
+ if (memo?.commitMemo?.()) {
421
+ memo.restoreMemo(true);
422
+ }
423
+ this.memo = null;
424
+ this._deactivateDraw(element);
425
+ resetElementCursor(element);
426
+ this._editData = null;
427
+ this._lastDragInfo = null;
428
+ this._previewData.preview = null;
429
+ this._previewData.isDrag = false;
430
+ if (this._previewData.timer) {
431
+ window.clearTimeout(this._previewData.timer);
432
+ this._previewData.timer = null;
433
+ }
434
+ this._resetLazyEditState();
435
+ this._clearCursor(element);
436
+ }
379
437
  _calculateCursor(element, _centerCanvas) {
380
438
  const enabledElement = getEnabledElement(element);
381
439
  const operationData = this.getOperationData(element);
@@ -123,9 +123,14 @@ class CircleScissorsTool extends LabelmapBaseTool {
123
123
  return true;
124
124
  };
125
125
  this._dragCallback = (evt) => {
126
- this.isDrawing = true;
127
126
  const eventDetail = evt.detail;
128
127
  const { element } = eventDetail;
128
+ const { currentPointsList } = eventDetail;
129
+ if (currentPointsList?.length > 1) {
130
+ this._cancelTouchDraw(element);
131
+ return;
132
+ }
133
+ this.isDrawing = true;
129
134
  const { currentPoints } = eventDetail;
130
135
  const currentCanvasPoints = currentPoints.canvas;
131
136
  const enabledElement = getEnabledElement(element);
@@ -58,6 +58,10 @@ export default class LabelmapBaseTool extends BaseTool {
58
58
  viewport: Types.IViewport;
59
59
  };
60
60
  static previewData?: PreviewData;
61
+ protected isDrawing?: boolean;
62
+ protected editData?: {
63
+ viewportIdsToRender?: string[];
64
+ } | null;
61
65
  protected memoMap: Map<string, LabelmapMemo.LabelmapMemo>;
62
66
  protected acceptedMemoIds: Map<string, {
63
67
  element: HTMLDivElement;
@@ -65,6 +69,7 @@ export default class LabelmapBaseTool extends BaseTool {
65
69
  }>;
66
70
  protected memo: LabelmapMemo.LabelmapMemo;
67
71
  constructor(toolProps: any, defaultToolProps: any);
72
+ protected _cancelTouchDraw(element: HTMLDivElement): void;
68
73
  protected _historyRedoHandler(evt: any): void;
69
74
  protected get _previewData(): PreviewData;
70
75
  hasPreviewData(): boolean;
@@ -17,6 +17,8 @@ import { fillInsideCircle } from './strategies/index.js';
17
17
  import getViewportLabelmapRenderMode from '../../stateManagement/segmentation/helpers/getViewportLabelmapRenderMode.js';
18
18
  import { getOrCreateLabelmapVolume, resolveLabelmapForSegment, } from '../../stateManagement/segmentation/helpers/labelmapSegmentationState.js';
19
19
  import getViewportICamera from '../../utilities/getViewportICamera.js';
20
+ import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds.js';
21
+ import { resetElementCursor } from '../../cursors/elementCursor.js';
20
22
  class LabelmapBaseTool extends BaseTool {
21
23
  constructor(toolProps, defaultToolProps) {
22
24
  super(toolProps, defaultToolProps);
@@ -29,6 +31,19 @@ class LabelmapBaseTool extends BaseTool {
29
31
  changedIndices: [],
30
32
  };
31
33
  }
34
+ _cancelTouchDraw(element) {
35
+ if (!this.isDrawing) {
36
+ return;
37
+ }
38
+ const viewportIdsToRender = this.editData?.viewportIdsToRender;
39
+ this._deactivateDraw(element);
40
+ resetElementCursor(element);
41
+ this.editData = null;
42
+ this.isDrawing = false;
43
+ if (viewportIdsToRender) {
44
+ triggerAnnotationRenderForViewportIds(viewportIdsToRender);
45
+ }
46
+ }
32
47
  _historyRedoHandler(evt) {
33
48
  const { id, operationType } = evt.detail;
34
49
  if (operationType !== 'labelmap') {
@@ -20,6 +20,7 @@ import { getPixelValueUnits } from '../../utilities/getPixelValueUnits.js';
20
20
  const { transformWorldToIndex } = csUtils;
21
21
  class RectangleROIStartEndThresholdTool extends RectangleROITool {
22
22
  constructor(toolProps = {}, defaultToolProps = {
23
+ supportedInteractionTypes: ['Mouse', 'Touch'],
23
24
  configuration: {
24
25
  storePointData: false,
25
26
  numSlicesToPropagate: 10,
@@ -115,9 +115,14 @@ class RectangleScissorsTool extends LabelmapBaseTool {
115
115
  return true;
116
116
  };
117
117
  this._dragCallback = (evt) => {
118
- this.isDrawing = true;
119
118
  const eventDetail = evt.detail;
120
119
  const { element } = eventDetail;
120
+ const { currentPointsList } = eventDetail;
121
+ if (currentPointsList?.length > 1) {
122
+ this._cancelTouchDraw(element);
123
+ return;
124
+ }
125
+ this.isDrawing = true;
121
126
  const { annotation, viewportIdsToRender, handleIndex } = this.editData;
122
127
  const { data } = annotation;
123
128
  const { currentPoints } = eventDetail;
@@ -23,7 +23,7 @@ class SegmentLabelTool extends BaseTool {
23
23
  },
24
24
  },
25
25
  }, defaultToolProps = {
26
- supportedInteractionTypes: ['Mouse', 'Touch'],
26
+ supportedInteractionTypes: ['Mouse'],
27
27
  configuration: {
28
28
  hoverTimeout: 100,
29
29
  searchRadius: 6,
@@ -9,7 +9,7 @@ import { state } from '../../store/state.js';
9
9
  import { ToolModes } from '../../enums/index.js';
10
10
  class SegmentSelectTool extends BaseTool {
11
11
  constructor(toolProps = {}, defaultToolProps = {
12
- supportedInteractionTypes: ['Mouse', 'Touch'],
12
+ supportedInteractionTypes: ['Mouse'],
13
13
  configuration: {
14
14
  hoverTimeout: 100,
15
15
  mode: SegmentSelectTool.SelectMode.Border,
@@ -110,9 +110,14 @@ class SphereScissorsTool extends LabelmapBaseTool {
110
110
  return true;
111
111
  };
112
112
  this._dragCallback = (evt) => {
113
- this.isDrawing = true;
114
113
  const eventDetail = evt.detail;
115
114
  const { element } = eventDetail;
115
+ const { currentPointsList } = eventDetail;
116
+ if (currentPointsList?.length > 1) {
117
+ this._cancelTouchDraw(element);
118
+ return;
119
+ }
120
+ this.isDrawing = true;
116
121
  const { currentPoints } = eventDetail;
117
122
  const currentCanvasPoints = currentPoints.canvas;
118
123
  const enabledElement = getEnabledElement(element);
@@ -0,0 +1,4 @@
1
+ export declare const TOUCH_PROXIMITY = 36;
2
+ export declare const MOUSE_PROXIMITY = 6;
3
+ export declare const TOUCH_TAP_MAX_CANVAS_DISTANCE = 24;
4
+ export declare const TOUCH_TAP_TOLERANCE_MS = 300;
@@ -0,0 +1,4 @@
1
+ export const TOUCH_PROXIMITY = 36;
2
+ export const MOUSE_PROXIMITY = 6;
3
+ export const TOUCH_TAP_MAX_CANVAS_DISTANCE = 24;
4
+ export const TOUCH_TAP_TOLERANCE_MS = 300;
@@ -7,4 +7,6 @@ declare function copyPointsList(points: ITouchPoints[]): ITouchPoints[];
7
7
  declare function copyPoints(points: ITouchPoints): ITouchPoints;
8
8
  declare function getMeanPoints(points: IPoints[]): IPoints;
9
9
  declare function getMeanTouchPoints(points: ITouchPoints[]): ITouchPoints;
10
- export { getMeanPoints, getMeanTouchPoints, copyPoints, copyPointsList, getDeltaDistanceBetweenIPoints, getDeltaPoints, getDeltaDistance, getDeltaRotation, };
10
+ declare function isMobile(): boolean;
11
+ export { getMeanPoints, getMeanTouchPoints, copyPoints, copyPointsList, getDeltaDistanceBetweenIPoints, getDeltaPoints, getDeltaDistance, getDeltaRotation, isMobile, };
12
+ export { TOUCH_PROXIMITY, MOUSE_PROXIMITY, TOUCH_TAP_MAX_CANVAS_DISTANCE, TOUCH_TAP_TOLERANCE_MS, } from './constants.js';
@@ -150,4 +150,10 @@ function _getDistance3D(point0, point1) {
150
150
  Math.pow(point0[1] - point1[1], 2) +
151
151
  Math.pow(point0[2] - point1[2], 2));
152
152
  }
153
- export { getMeanPoints, getMeanTouchPoints, copyPoints, copyPointsList, getDeltaDistanceBetweenIPoints, getDeltaPoints, getDeltaDistance, getDeltaRotation, };
153
+ function isMobile() {
154
+ return (typeof window !== 'undefined' &&
155
+ typeof window.matchMedia === 'function' &&
156
+ window.matchMedia('(any-pointer:coarse)').matches);
157
+ }
158
+ export { getMeanPoints, getMeanTouchPoints, copyPoints, copyPointsList, getDeltaDistanceBetweenIPoints, getDeltaPoints, getDeltaDistance, getDeltaRotation, isMobile, };
159
+ export { TOUCH_PROXIMITY, MOUSE_PROXIMITY, TOUCH_TAP_MAX_CANVAS_DISTANCE, TOUCH_TAP_TOLERANCE_MS, } from './constants.js';
@@ -1 +1 @@
1
- export declare const version = "5.6.14";
1
+ export declare const version = "5.7.1";
@@ -1 +1 @@
1
- export const version = '5.6.14';
1
+ export const version = '5.7.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cornerstonejs/tools",
3
- "version": "5.6.14",
3
+ "version": "5.7.1",
4
4
  "description": "Cornerstone3D Tools",
5
5
  "types": "./dist/esm/index.d.ts",
6
6
  "module": "./dist/esm/index.js",
@@ -126,11 +126,11 @@
126
126
  "lodash.get": "4.4.2"
127
127
  },
128
128
  "devDependencies": {
129
- "@cornerstonejs/core": "5.6.14",
129
+ "@cornerstonejs/core": "5.7.1",
130
130
  "canvas": "3.2.0"
131
131
  },
132
132
  "peerDependencies": {
133
- "@cornerstonejs/core": "5.6.14",
133
+ "@cornerstonejs/core": "5.7.1",
134
134
  "@kitware/vtk.js": "36.4.1",
135
135
  "@types/d3-array": "3.2.1",
136
136
  "@types/d3-interpolate": "3.0.4",
@@ -149,5 +149,5 @@
149
149
  "type": "individual",
150
150
  "url": "https://ohif.org/donate"
151
151
  },
152
- "gitHead": "28a786f97cef363f62f94f38e95d03f4bcccba2e"
152
+ "gitHead": "14841bb671002953b4d98da0333c65dc8e8156ef"
153
153
  }