All files / packages/core/src/RenderingEngine/helpers createVolumeActor.ts

94.28% Statements 33/35
80.76% Branches 21/26
100% Functions 5/5
100% Lines 28/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                                                              78x     702x 78x 78x   78x   78x   156x           78x   78x   78x 22x     78x 78x   78x         78x 2x           78x 156x       78x 19x     78x 56x 78x   78x 1x               56x         56x                 56x        
import vtkVolume from '@kitware/vtk.js/Rendering/Core/Volume';
 
import { VolumeActor } from './../../types/IActor';
import { VoiModifiedEventDetail } from './../../types/EventTypes';
import { loadVolume } from '../../loaders/volumeLoader';
import createVolumeMapper from './createVolumeMapper';
import BlendModes from '../../enums/BlendModes';
import { triggerEvent } from '../../utilities';
import { Events } from '../../enums';
import setDefaultVolumeVOI from './setDefaultVolumeVOI';
 
interface createVolumeActorInterface {
  volumeId: string;
  callback?: ({
    volumeActor,
    volumeId,
  }: {
    volumeActor: VolumeActor;
    volumeId: string;
  }) => void;
  blendMode?: BlendModes;
}
 
/**
 * Given a volumeId, it creates a vtk volume actor and returns it. If
 * callback is provided, it will be called with the volume actor and the
 * volumeId. If blendMode is provided, it will be set on the volume actor.
 *
 * @param props - createVolumeActorInterface
 * @returns A promise that resolves to a VolumeActor.
 */
async function createVolumeActor(
  props: createVolumeActorInterface,
  element: HTMLDivElement,
  viewportId: string,
  suppressEvents = false,
  use16BitTexture = false
): Promise<VolumeActor> {
  const { volumeId, callback, blendMode } = props;
 
  const imageVolumeE = await loadVolume(volumeId);
 
  if (!imageVolume) {
    throw new Error(
      `imageVolume with id: ${imageVolume.volumeId} does not exist`
    );
  }
 
  const { imageData, vtkOpenGLTexture } = imageVolume;
 
  const volumeMapper = createVolumeMapper(imageData, vtkOpenGLTexture);
 
  if (blendMode) {
    volumeMapper.setBlendMode(blendMode);
  }
 
  const volumeActor = vtkVolume.newInstance();
  volumeActor.setMapper(volumeMapper);
 
  const numberOfComponents = imageData
    .getPointData()
    .getScalars()
    .getNumberOfComponents();
 
  if (numberOfComponents === 3) {
    volumeActor.getProperty().setIndependentComponents(false);
  }
 
  // If the volume is composed of imageIds, we can apply a default VOI based
  // on either the metadata or the min/max of the middle slice. Example of other
  // types of volumes which might not be composed of imageIds would be e.g., nrrd, nifti
  E// format volumes
  if (imageVolume.imageIds) {
    await setDefaultVolumeVOI(volumeActor, imageVolume, use16BitTexture);
  }
 
  if (callback) {
    callback({ volumeActor, volumeId });
  }
 
  if (!suppressEvents) {
    triggerVOIModified(element, viewportId, volumeActor, volumeId);
  }
 
  return volumeActor;
}
 
function triggerVOIModified(
  element: HTMLDivElement,
  viewportId: string,
  volumeActor: VolumeActor,
  volumeId: string
) {
  const voiRange = volumeActor
    .getProperty()
    .getRGBTransferFunction(0)
    .getRange();
 
  const voiModifiedEventDetail: VoiModifiedEventDetail = {
    viewportId,
    range: {
      lower: voiRange[0],
      upper: voiRange[1],
    },
    volumeId,
  };
 
  triggerEvent(element, Events.VOI_MODIFIED, voiModifiedEventDetail);
}
 
export default createVolumeActor;