All files / packages/tools/src/utilities scroll.ts

75% Statements 21/28
55% Branches 11/20
100% Functions 2/2
75% Lines 21/28

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 115 116 117 118 119 120                                                    6x   6x       6x             6x 6x   6x 5x 1x 1x                           1x     1x   1x       1x 1x   1x                 1x       1x   1x     1x                   1x                         1x              
import {
  StackViewport,
  Types,
  VolumeViewport,
  eventTarget,
  EVENTS,
  utilities as csUtils,
  getEnabledElement,
  VideoViewport,
} from '@cornerstonejs/core';
import { ScrollOptions, EventTypes } from '../types';
 
/**
 * It scrolls one slice in the Stack or Volume Viewport, it uses the options provided
 * to determine the slice to scroll to. For Stack Viewport, it scrolls in the 1 or -1
 * direction, for Volume Viewport, it uses the camera and focal point to determine the
 * slice to scroll to based on the spacings.
 * @param viewport - The viewport in which to scroll
 * @param options - Options to use for scrolling, including direction, invert, and volumeId
 * @returns
 */
export default function scroll(
  viewport: Types.IViewport,
  options: ScrollOptions
): void {
  // check if viewport is disabled then throw error
  const enabledElement = getEnabledElement(viewport.element);
 
  Iif (!enabledElement) {
    throw new Error('Scroll::Viewport is not enabled (it might be disabled)');
  }
 
  Iif (
    viewport instanceof StackViewport &&
    viewport.getImageIds().length === 0
  ) {
    throw new Error('Scroll::Stack Viewport has no images');
  }
 
  const { type: viewportType } = viewport;
  const { volumeId, delta, scrollSlabs } = options;
 
  if (viewport instanceof StackViewport) {
    viewport.scroll(delta, options.debounceLoading, options.loop);
  } else if (viewport instanceof VolumeViewport) {
    scrollVolume(viewport, volumeId, delta, scrollSlabs);
  } else Eif (viewport instanceof VideoViewport) {
    viewport.scroll(delta);
  } else {
    throw new Error(`Not implemented for Viewport Type: ${viewportType}`);
  }
}
 
export function scrollVolume(
  viewport: VolumeViewport,
  volumeId: string,
  delta: number,
  scrollSlabs = false
) {
  const useSlabThickness = scrollSlabs;
 
  const { numScrollSteps, currentStepIndex, sliceRangeInfo } =
    csUtils.getVolumeViewportScrollInfo(viewport, volumeId, useSlabThickness);
 
  Iif (!sliceRangeInfo) {
    return;
  }
 
  const { sliceRange, spacingInNormalDirection, camera } = sliceRangeInfo;
  const { focalPoint, viewPlaneNormal, position } = camera;
 
  const { newFocalPoint, newPosition } = csUtils.snapFocalPointToSlice(
    focalPoint,
    position,
    sliceRange,
    viewPlaneNormal,
    spacingInNormalDirection,
    delta
  );
 
  viewport.setCamera({
    focalPoint: newFocalPoint,
    position: newPosition,
  });
  viewport.render();
 
  const desiredStepIndex = currentStepIndex + delta;
 
  const VolumeScrollEventDetail: EventTypes.VolumeScrollOutOfBoundsEventDetail =
    {
      volumeId,
      viewport,
      delta,
      desiredStepIndex,
      currentStepIndex,
      numScrollSteps,
      currentImageId: viewport.getCurrentImageId(),
    };
 
  Iif (
    (desiredStepIndex > numScrollSteps || desiredStepIndex < 0) &&
    viewport.getCurrentImageId() // Check that we are in the plane of acquistion
  ) {
    // One common use case of this trigger might be to load the next
    // volume in a time series or the next segment of a partially loaded volume.
 
    csUtils.triggerEvent(
      eventTarget,
      EVENTS.VOLUME_SCROLL_OUT_OF_BOUNDS,
      VolumeScrollEventDetail
    );
  } else {
    csUtils.triggerEvent(
      eventTarget,
      EVENTS.VOLUME_VIEWPORT_SCROLL,
      VolumeScrollEventDetail
    );
  }
}