All files / core/src/RenderingEngine/GenericViewport/ECG ecgViewportCamera.ts

0% Statements 0/19
0% Branches 0/16
0% Functions 0/8
0% Lines 0/19

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 121 122 123                                                                                                                                                                                                                                                     
import type { Point2 } from '../../../types';
import type { ViewAnchor } from '../ViewportCameraTypes';
import type { ECGViewState, RenderWindowMetrics } from './ECGViewportTypes';
 
export interface ECGCanvasMapping {
  anchorWorld: [number, number];
  anchorCanvas: ViewAnchor;
  canvasHeight: number;
  canvasWidth: number;
  centeredXOffset: number;
  centeredYOffset: number;
  effectiveRatio: number;
  xOffset: number;
  yOffset: number;
}
 
export function createDefaultECGViewState(args: {
  timeRange: [number, number];
  valueRange: [number, number];
}): ECGViewState {
  return {
    timeRange: [...args.timeRange] as [number, number],
    valueRange: [...args.valueRange] as [number, number],
    scrollOffset: 0,
    anchorCanvas: [0.5, 0.5],
    scale: 1,
    scaleMode: 'fit',
    rotation: 0,
  };
}
 
export function normalizeECGViewState(camera: ECGViewState): ECGViewState {
  return {
    timeRange: [...camera.timeRange] as [number, number],
    valueRange: [...camera.valueRange] as [number, number],
    ...(camera.scrollOffset !== undefined
      ? { scrollOffset: camera.scrollOffset }
      : {}),
    anchorCanvas: cloneAnchorCanvas(camera.anchorCanvas ?? [0.5, 0.5]),
    scale: Math.max(camera.scale ?? 1, 0.001),
    scaleMode: 'fit',
    rotation: 0,
    ...(camera.anchorWorld
      ? {
          anchorWorld: clonePoint(camera.anchorWorld),
        }
      : {}),
  };
}
 
export function resolveECGCanvasMapping(args: {
  metrics: RenderWindowMetrics;
  camera?: ECGViewState;
  canvas: HTMLCanvasElement;
}): ECGCanvasMapping {
  const { metrics, camera, canvas } = args;
  const scale = Math.max(camera?.scale ?? 1, 0.001);
  const effectiveRatio = metrics.worldToCanvasRatio * scale;
  const drawWidth = metrics.ecgWidth * effectiveRatio;
  const drawHeight = metrics.ecgHeight * effectiveRatio;
  const centeredXOffset = (canvas.clientWidth - drawWidth) / 2;
  const centeredYOffset = (canvas.clientHeight - drawHeight) / 2;
  const anchorCanvas = cloneAnchorCanvas(camera?.anchorCanvas ?? [0.5, 0.5]);
  const anchorWorld =
    clonePoint(camera?.anchorWorld) ??
    ([metrics.ecgWidth / 2, metrics.ecgHeight / 2] as [number, number]);
  const xOffset =
    canvas.clientWidth * anchorCanvas[0] - anchorWorld[0] * effectiveRatio;
  const yOffset =
    canvas.clientHeight * anchorCanvas[1] - anchorWorld[1] * effectiveRatio;
 
  return {
    anchorWorld,
    anchorCanvas,
    canvasHeight: canvas.clientHeight,
    canvasWidth: canvas.clientWidth,
    centeredXOffset,
    centeredYOffset,
    effectiveRatio,
    xOffset,
    yOffset,
  };
}
 
export function getPanForECGCanvasMapping(mapping: ECGCanvasMapping): Point2 {
  return [
    mapping.xOffset - mapping.centeredXOffset,
    mapping.yOffset - mapping.centeredYOffset,
  ];
}
 
export function getAnchorWorldForPan(
  pan: Point2,
  mapping: ECGCanvasMapping
): [number, number] {
  return [
    (mapping.canvasWidth * mapping.anchorCanvas[0] -
      (mapping.centeredXOffset + pan[0])) /
      mapping.effectiveRatio,
    (mapping.canvasHeight * mapping.anchorCanvas[1] -
      (mapping.centeredYOffset + pan[1])) /
      mapping.effectiveRatio,
  ];
}
 
export function getAnchorWorldForCanvasPoint(
  canvasPoint: Point2,
  mapping: ECGCanvasMapping
): [number, number] {
  return [
    (canvasPoint[0] - mapping.xOffset) / mapping.effectiveRatio,
    (canvasPoint[1] - mapping.yOffset) / mapping.effectiveRatio,
  ];
}
 
function clonePoint(point?: [number, number]): [number, number] | undefined {
  return point ? [point[0], point[1]] : undefined;
}
 
function cloneAnchorCanvas(anchorCanvas: ViewAnchor): ViewAnchor {
  return [anchorCanvas[0], anchorCanvas[1]];
}