All files / utils/test testUtilsMouseEvents.ts

96.66% Statements 29/30
88.88% Branches 8/9
100% Functions 7/7
96.55% Lines 28/29

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114        1x     210x 210x                                                 210x 210x 210x 420x 210x 210x   210x                                                                 35x 35x 35x       35x     35x 35x   35x 19x     35x     35x 28x   7x     35x 35x 35x   35x     35x     35x          
import type { vtkImageData } from '@kitware/vtk.js/Common/DataModel/ImageData';
import { getOrCreateCanvas } from '@cornerstonejs/core';
import * as cornerstoneTools from '@cornerstonejs/tools';
 
const { Events } = cornerstoneTools.Enums;
 
function canvasPointsToPagePoints(DomCanvasElement, canvasPoint) {
  const rect = DomCanvasElement.getBoundingClientRect();
  return [
    canvasPoint[0] + rect.left + window.pageXOffset,
    canvasPoint[1] + rect.top + window.pageYOffset,
  ];
}
 
/**
 * This function uses the imageData being displayed on the viewport (the default image) and
 * an index (IJK) on the image to normalize the mouse event details.
 * It should be noted that the normalization is required since client and page XY
 * cannot accept a float. Therefore, for the requested index, canvas coordinate
 * will get calculated and normalized (rounded) to enable normalized client/page XY
 *
 * @param {vtkImageData} imageData
 * @param {[number, number,number]} index - IJK index of the point to click
 * @param {HTMLDivElement} element - the canvas to be clicked on
 * @param {IStackViewport|IVolumeViewport} viewport
 * @returns pageX, pageY, clientX, clientY, worldCoordinate
 */
function createNormalizedMouseEvent(
  imageData: vtkImageData,
  index,
  element,
  viewport
) {
  const canvas = getOrCreateCanvas(element);
  const tempWorld1 = imageData.indexToWorld(index);
  const tempCanvasPoint1 = viewport.worldToCanvas(tempWorld1);
  const canvasPoint1 = tempCanvasPoint1.map((p) => Math.round(p));
  const [pageX, pageY] = canvasPointsToPagePoints(canvas, canvasPoint1);
  const worldCoord = viewport.canvasToWorld(canvasPoint1);
 
  return {
    pageX,
    pageY,
    clientX: pageX,
    clientY: pageY,
    worldCoord,
  };
}
 
/**
 * Asynchronously dispatches a mouse down followed by a mouse up on the given element.
 * Since mouse down events are performed on a timeout to detect potential
 * double clicks, the mouse up event is not triggered until the mouse down
 * event has been processed. An optional callback is invoked after the mouse
 * down is triggered but before the mouse up. An optional callback is invoked
 * after the mouse up has fired.
 *
 * @param element the element to dispatch to
 * @param mouseDownEvent the mouse down event to dispatch
 * @param mouseUpEvent the mouse up event to dispatch
 * @param betweenDownAndUpCallback optional callback between the down and up
 * @param afterDownAndUpCallback optional callback after the up
 * @param isActivate is it a down and up to activate (e.g. a tool) or simple down and up (e.g. to select)
 * @returns a Promise for the eventual completion of the mouse down and up
 */
function performMouseDownAndUp(
  element: HTMLElement,
  mouseDownEvent: MouseEvent,
  mouseUpEvent: MouseEvent,
  betweenDownAndUpCallback: () => unknown = null,
  afterDownAndUpCallback: () => unknown = null,
  isActivate = true
): Promise<void> {
  return new Promise<void>((resolve) => {
    const mouseDownListener = function () {
      element.removeEventListener(
        Events.MOUSE_DOWN_ACTIVATE,
        mouseDownListener
      );
      element.removeEventListener(Events.MOUSE_DOWN, mouseDownListener);
 
      // It could be a click or an up.
      element.addEventListener(Events.MOUSE_UP, mouseUpListener);
      element.addEventListener(Events.MOUSE_CLICK, mouseUpListener);
 
      if (betweenDownAndUpCallback) {
        betweenDownAndUpCallback();
      }
 
      document.dispatchEvent(mouseUpEvent);
    };
 
    if (isActivate) {
      element.addEventListener(Events.MOUSE_DOWN_ACTIVATE, mouseDownListener);
    } else {
      element.addEventListener(Events.MOUSE_DOWN, mouseDownListener);
    }
 
    const mouseUpListener = function () {
      element.removeEventListener(Events.MOUSE_UP, mouseUpListener);
      element.removeEventListener(Events.MOUSE_CLICK, mouseUpListener);
 
      Iif (afterDownAndUpCallback) {
        afterDownAndUpCallback();
      }
      resolve();
    };
 
    element.dispatchEvent(mouseDownEvent);
  });
}
 
export { createNormalizedMouseEvent, performMouseDownAndUp };