All files / extensions/cornerstone/src/services/ViewportService/adapter getViewportAdapter.ts

81.81% Statements 9/11
70% Branches 7/10
75% Functions 3/4
81.81% Lines 9/11

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          182x                         69477x     69477x 69477x 604x         604x   69477x                   350x                     20234x                      
import { utilities as csUtils, Types as CoreTypes } from '@cornerstonejs/core';
import type { IViewportAdapter } from './IViewportAdapter';
import { LegacyViewportAdapter } from './LegacyViewportAdapter';
import { NextViewportAdapter } from './NextViewportAdapter';
 
const adapterCache = new WeakMap<object, IViewportAdapter>();
 
/**
 * Resolves the IViewportAdapter for a cornerstone viewport. This is THE ONE
 * place (outside the segmentation backend family) allowed to call
 * `csUtils.isGenericViewport` — all other code must consume the adapter (or
 * the `isNextViewport` predicate below) instead of branching on the raw
 * viewport surface. Enforced by scripts/check-next-viewport-boundaries.sh.
 *
 * Adapters are stateless wrappers over the viewport, cached per viewport
 * instance, so calling this in render paths is cheap.
 */
export function getViewportAdapter(viewport: unknown): IViewportAdapter {
  Iif (!viewport || typeof viewport !== 'object') {
    throw new Error('getViewportAdapter: a viewport instance is required');
  }
  let adapter = adapterCache.get(viewport);
  if (!adapter) {
    adapter = csUtils.isGenericViewport(viewport)
      ? new NextViewportAdapter(viewport as ConstructorParameters<typeof NextViewportAdapter>[0])
      : new LegacyViewportAdapter(
          viewport as ConstructorParameters<typeof LegacyViewportAdapter>[0]
        );
    adapterCache.set(viewport, adapter);
  }
  return adapter;
}
 
/**
 * The per-viewport lane predicate, for the few dispatchers (viewport
 * operations, segmentation backend) that hold their own per-lane
 * implementations. Everyone else should call adapter methods instead of
 * branching on this.
 */
export function isNextViewport(viewport: unknown): boolean {
  return csUtils.isGenericViewport(viewport);
}
 
/**
 * True for any viewport that renders volume content and therefore supports
 * volume-only appearance controls (threshold, per-layer opacity): a legacy
 * ORTHOGRAPHIC viewport, or a native ("next") viewport whose active binding is
 * a volume. A native MPR/volume viewport runs as PLANAR_NEXT (not
 * ORTHOGRAPHIC), so legacy type guards alone miss it.
 */
export function isVolumeRenderingViewport(viewport: unknown): boolean {
  return !!viewport && getViewportAdapter(viewport).isVolumeRendering();
}
 
/**
 * World-space focal point (current slice center) for any viewport. Exposed as
 * a free function because it is part of the extension's public API (consumed
 * by tmtv).
 */
export function getViewportFocalPoint(viewport: unknown): CoreTypes.Point3 | undefined {
  return viewport ? getViewportAdapter(viewport).getFocalPoint() : undefined;
}