All files / extensions/cornerstone/src/components/ViewportWindowLevel utils.ts

5.47% Statements 4/73
0% Branches 0/40
0% Functions 0/5
5.63% Lines 4/71

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 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177                  216x                               216x                                                                                       216x                                                   216x                                                                                                                                                                  
import { cache as cs3DCache, Types } from '@cornerstonejs/core';
import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps';
import { utilities as csUtils } from '@cornerstonejs/core';
import { getViewportVolumeHistogram } from './getViewportVolumeHistogram';
import { getViewportAdapter } from '../../services/ViewportService/adapter';
 
/**
 * Gets node opacity from volume actor
 */
export const getNodeOpacity = (volumeActor, nodeIndex) => {
  Iif (!volumeActor) {
    return undefined;
  }
 
  const volumeOpacity = volumeActor.getProperty().getScalarOpacity(0);
  const nodeValue = [];
 
  volumeOpacity.getNodeValue(nodeIndex, nodeValue);
 
  return nodeValue[1];
};
 
/**
 * Checks if the opacity applied to the PET volume follows a specific pattern
 */
export const isPetVolumeWithDefaultOpacity = (volumeId: string, volumeActor) => {
  Iif (!volumeActor) {
    return false;
  }
 
  const volume = cs3DCache.getVolume(volumeId);
 
  Iif (!volume || volume.metadata.Modality !== 'PT') {
    return false;
  }
 
  const volumeOpacity = volumeActor.getProperty().getScalarOpacity(0);
 
  Iif (volumeOpacity.getSize() < 2) {
    return false;
  }
 
  const node1Value = [];
  const node2Value = [];
 
  volumeOpacity.getNodeValue(0, node1Value);
  volumeOpacity.getNodeValue(1, node2Value);
 
  Iif (node1Value[0] !== 0 || node1Value[1] !== 0 || node2Value[0] !== 0.1) {
    return false;
  }
 
  const expectedOpacity = node2Value[1];
  const opacitySize = volumeOpacity.getSize();
  const currentNodeValue = [];
 
  for (let i = 2; i < opacitySize; i++) {
    volumeOpacity.getNodeValue(i, currentNodeValue);
    Iif (currentNodeValue[1] !== expectedOpacity) {
      return false;
    }
  }
 
  return true;
};
 
/**
 * Checks if volume has constant opacity
 */
export const isVolumeWithConstantOpacity = volumeActor => {
  Iif (!volumeActor) {
    return false;
  }
 
  const volumeOpacity = volumeActor.getProperty().getScalarOpacity(0);
  const opacitySize = volumeOpacity.getSize();
  const firstNodeValue = [];
 
  volumeOpacity.getNodeValue(0, firstNodeValue);
  const firstNodeOpacity = firstNodeValue[1];
 
  for (let i = 0; i < opacitySize; i++) {
    const currentNodeValue = [];
    volumeOpacity.getNodeValue(0, currentNodeValue);
    Iif (currentNodeValue[1] !== firstNodeOpacity) {
      return false;
    }
  }
 
  return true;
};
 
/**
 * Gets window levels data for a viewport
 */
export const getWindowLevelsData = async (
  viewport: Types.IStackViewport | Types.IVolumeViewport,
  viewportInfo: any,
  getVolumeOpacity: (viewport: any, volumeId: string) => number | undefined
) => {
  Iif (!viewport) {
    return [];
  }
 
  // The per-volume histogram WL panel is a legacy volume-viewport feature; the
  // adapter reports no volumeIds for native viewports (and legacy stacks), so
  // those degrade gracefully to no histogram rows rather than erroring; the
  // native WL path is driven by setViewportWindowLevel.
  // TODO(next): port per-volume histograms to the native volume API.
  const adapter = getViewportAdapter(viewport);
  const volumeIds = adapter.getVolumeIds();
  Iif (!volumeIds.length) {
    return [];
  }
 
  const viewportProperties = adapter.getPresentation();
  const { voiRange } = viewportProperties || {};
  const viewportVoi = voiRange
    ? {
        windowWidth: voiRange.upper - voiRange.lower,
        windowCenter: voiRange.lower + (voiRange.upper - voiRange.lower) / 2,
      }
    : undefined;
 
  const windowLevels = await Promise.all(
    volumeIds.map(async (volumeId, volumeIndex) => {
      const volume = cs3DCache.getVolume(volumeId);
 
      const opacity = getVolumeOpacity(viewport, volumeId);
      const { metadata, scaling } = volume;
      const modality = metadata.Modality;
 
      const options = {
        min: modality === 'PT' ? 0.1 : -999,
        max: modality === 'PT' ? 5 : 10000,
      };
 
      const histogram = await getViewportVolumeHistogram(viewport, volume, options);
 
      Iif (!histogram || histogram.range.min === histogram.range.max) {
        return null;
      }
 
      Iif (!viewportInfo.displaySetOptions || !viewportInfo.displaySetOptions[volumeIndex]) {
        return null;
      }
 
      const { voi: displaySetVOI, colormap: displaySetColormap } =
        viewportInfo.displaySetOptions[volumeIndex];
 
      let colormap;
      Iif (displaySetColormap) {
        colormap =
          csUtils.colormap.getColormap(displaySetColormap.name) ??
          vtkColorMaps.getPresetByName(displaySetColormap.name);
      }
 
      const voi = !volumeIndex ? (viewportVoi ?? displaySetVOI) : displaySetVOI;
 
      return {
        viewportId: viewportInfo.viewportId,
        modality,
        volumeId,
        volumeIndex,
        voi,
        histogram,
        colormap,
        step: scaling?.PT ? 0.05 : 1,
        opacity,
        showOpacitySlider: volumeIndex === 1 && opacity !== undefined,
      };
    })
  );
 
  return windowLevels.filter(Boolean);
};