851 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Plaintext
		
	
	
			
		
		
	
	
			851 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Plaintext
		
	
	
import {
 | 
						|
  RenderingEngine,
 | 
						|
  Types,
 | 
						|
  Enums,
 | 
						|
  setVolumesForViewports,
 | 
						|
  volumeLoader,
 | 
						|
  getRenderingEngine,
 | 
						|
} from '@cornerstonejs/core';
 | 
						|
import {
 | 
						|
  initDemo,
 | 
						|
  createImageIdsAndCacheMetaData,
 | 
						|
  setTitleAndDescription,
 | 
						|
  setPetColorMapTransferFunctionForVolumeActor,
 | 
						|
  setPetTransferFunctionForVolumeActor,
 | 
						|
  setCtTransferFunctionForVolumeActor,
 | 
						|
  addDropdownToToolbar,
 | 
						|
  addButtonToToolbar,
 | 
						|
} from '../../../../utils/demo/helpers';
 | 
						|
import * as cornerstoneTools from '@cornerstonejs/tools';
 | 
						|
 | 
						|
const {
 | 
						|
  ToolGroupManager,
 | 
						|
  Enums: csToolsEnums,
 | 
						|
  WindowLevelTool,
 | 
						|
  PanTool,
 | 
						|
  ZoomTool,
 | 
						|
  StackScrollMouseWheelTool,
 | 
						|
  synchronizers,
 | 
						|
  MIPJumpToClickTool,
 | 
						|
  VolumeRotateMouseWheelTool,
 | 
						|
  CrosshairsTool,
 | 
						|
  TrackballRotateTool,
 | 
						|
} = cornerstoneTools;
 | 
						|
 | 
						|
const { MouseBindings } = csToolsEnums;
 | 
						|
const { ViewportType, BlendModes } = Enums;
 | 
						|
 | 
						|
const { createCameraPositionSynchronizer, createVOISynchronizer } =
 | 
						|
  synchronizers;
 | 
						|
 | 
						|
let renderingEngine;
 | 
						|
const wadoRsRoot = 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb';
 | 
						|
const StudyInstanceUID =
 | 
						|
  '1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463';
 | 
						|
const renderingEngineId = 'myRenderingEngine';
 | 
						|
const volumeLoaderScheme = 'cornerstoneStreamingImageVolume'; // Loader id which defines which volume loader to use
 | 
						|
const ctVolumeName = 'CT_VOLUME_ID'; // Id of the volume less loader prefix
 | 
						|
const ctVolumeId = `${volumeLoaderScheme}:${ctVolumeName}`; // VolumeId with loader id + volume id
 | 
						|
const ptVolumeName = 'PT_VOLUME_ID';
 | 
						|
const ptVolumeId = `${volumeLoaderScheme}:${ptVolumeName}`;
 | 
						|
const ctToolGroupId = 'CT_TOOLGROUP_ID';
 | 
						|
const ptToolGroupId = 'PT_TOOLGROUP_ID';
 | 
						|
const fusionToolGroupId = 'FUSION_TOOLGROUP_ID';
 | 
						|
const mipToolGroupUID = 'MIP_TOOLGROUP_ID';
 | 
						|
let ctImageIds;
 | 
						|
let ptImageIds;
 | 
						|
let ctVolume;
 | 
						|
let ptVolume;
 | 
						|
const axialCameraSynchronizerId = 'AXIAL_CAMERA_SYNCHRONIZER_ID';
 | 
						|
const sagittalCameraSynchronizerId = 'SAGITTAL_CAMERA_SYNCHRONIZER_ID';
 | 
						|
const coronalCameraSynchronizerId = 'CORONAL_CAMERA_SYNCHRONIZER_ID';
 | 
						|
const ctVoiSynchronizerId = 'CT_VOI_SYNCHRONIZER_ID';
 | 
						|
const ptVoiSynchronizerId = 'PT_VOI_SYNCHRONIZER_ID';
 | 
						|
let axialCameraPositionSynchronizer;
 | 
						|
let sagittalCameraPositionSynchronizer;
 | 
						|
let coronalCameraPositionSynchronizer;
 | 
						|
let ctVoiSynchronizer;
 | 
						|
let ptVoiSynchronizer;
 | 
						|
let mipToolGroup;
 | 
						|
const viewportIds = {
 | 
						|
  CT: { AXIAL: 'CT_AXIAL', SAGITTAL: 'CT_SAGITTAL', CORONAL: 'CT_CORONAL' },
 | 
						|
  PT: { AXIAL: 'PT_AXIAL', SAGITTAL: 'PT_SAGITTAL', CORONAL: 'PT_CORONAL' },
 | 
						|
  FUSION: {
 | 
						|
    AXIAL: 'FUSION_AXIAL',
 | 
						|
    SAGITTAL: 'FUSION_SAGITTAL',
 | 
						|
    CORONAL: 'FUSION_CORONAL',
 | 
						|
  },
 | 
						|
  PETMIP: {
 | 
						|
    CORONAL: 'PET_MIP_CORONAL',
 | 
						|
  },
 | 
						|
};
 | 
						|
 | 
						|
// ======== Set up page ======== //
 | 
						|
setTitleAndDescription(
 | 
						|
  'PET-CT',
 | 
						|
  'PT-CT fusion layout with Crosshairs, and synchronized cameras, CT W/L and PET threshold'
 | 
						|
);
 | 
						|
 | 
						|
const optionsValues = [WindowLevelTool.toolName, CrosshairsTool.toolName];
 | 
						|
 | 
						|
// ============================= //
 | 
						|
addDropdownToToolbar({
 | 
						|
  options: { values: optionsValues, defaultValue: WindowLevelTool.toolName },
 | 
						|
  onSelectedValueChange: (toolNameAsStringOrNumber) => {
 | 
						|
    const toolName = String(toolNameAsStringOrNumber);
 | 
						|
 | 
						|
    [ctToolGroupId, ptToolGroupId, fusionToolGroupId].forEach((toolGroupId) => {
 | 
						|
      const toolGroup = ToolGroupManager.getToolGroup(toolGroupId);
 | 
						|
 | 
						|
      // Set the other tools disabled so we don't get conflicts.
 | 
						|
      // Note we only strictly need to change the one which is currently active.
 | 
						|
 | 
						|
      if (toolName === WindowLevelTool.toolName) {
 | 
						|
        // Set crosshairs passive so they are still interactable
 | 
						|
        toolGroup.setToolPassive(CrosshairsTool.toolName);
 | 
						|
        toolGroup.setToolActive(WindowLevelTool.toolName, {
 | 
						|
          bindings: [{ mouseButton: MouseBindings.Primary }],
 | 
						|
        });
 | 
						|
      } else {
 | 
						|
        toolGroup.setToolDisabled(WindowLevelTool.toolName);
 | 
						|
        toolGroup.setToolActive(CrosshairsTool.toolName, {
 | 
						|
          bindings: [{ mouseButton: MouseBindings.Primary }],
 | 
						|
        });
 | 
						|
      }
 | 
						|
    });
 | 
						|
  },
 | 
						|
});
 | 
						|
 | 
						|
const resizeObserver = new ResizeObserver(() => {
 | 
						|
  console.log('Size changed');
 | 
						|
 | 
						|
  renderingEngine = getRenderingEngine(renderingEngineId);
 | 
						|
 | 
						|
  if (renderingEngine) {
 | 
						|
    renderingEngine.resize(true, false);
 | 
						|
  }
 | 
						|
});
 | 
						|
 | 
						|
const viewportGrid = document.createElement('div');
 | 
						|
 | 
						|
viewportGrid.style.display = 'grid';
 | 
						|
viewportGrid.style.gridTemplateRows = `[row1-start] 33% [row2-start] 33% [row3-start] 33% [end]`;
 | 
						|
viewportGrid.style.gridTemplateColumns = `[col1-start] 20% [col2-start] 20% [col3-start] 20% [col4-start] 20% [col5-start] 20%[end]`;
 | 
						|
viewportGrid.style.width = '95vw';
 | 
						|
viewportGrid.style.height = '80vh';
 | 
						|
 | 
						|
const content = document.getElementById('content');
 | 
						|
 | 
						|
content.appendChild(viewportGrid);
 | 
						|
 | 
						|
const element1_1 = document.createElement('div');
 | 
						|
const element1_2 = document.createElement('div');
 | 
						|
const element1_3 = document.createElement('div');
 | 
						|
const element2_1 = document.createElement('div');
 | 
						|
const element2_2 = document.createElement('div');
 | 
						|
const element2_3 = document.createElement('div');
 | 
						|
const element3_1 = document.createElement('div');
 | 
						|
const element3_2 = document.createElement('div');
 | 
						|
const element3_3 = document.createElement('div');
 | 
						|
const element_mip = document.createElement('div');
 | 
						|
 | 
						|
// Place main 3x3 viewports
 | 
						|
element1_1.style.gridColumnStart = '1';
 | 
						|
element1_1.style.gridRowStart = '1';
 | 
						|
element1_2.style.gridColumnStart = '2';
 | 
						|
element1_2.style.gridRowStart = '1';
 | 
						|
element1_3.style.gridColumnStart = '3';
 | 
						|
element1_3.style.gridRowStart = '1';
 | 
						|
element2_1.style.gridColumnStart = '1';
 | 
						|
element2_1.style.gridRowStart = '2';
 | 
						|
element2_2.style.gridColumnStart = '2';
 | 
						|
element2_2.style.gridRowStart = '2';
 | 
						|
element2_3.style.gridColumnStart = '3';
 | 
						|
element2_3.style.gridRowStart = '2';
 | 
						|
element3_1.style.gridColumnStart = '1';
 | 
						|
element3_1.style.gridRowStart = '3';
 | 
						|
element3_2.style.gridColumnStart = '2';
 | 
						|
element3_2.style.gridRowStart = '3';
 | 
						|
element3_3.style.gridColumnStart = '3';
 | 
						|
element3_3.style.gridRowStart = '3';
 | 
						|
 | 
						|
// Place MIP viewport
 | 
						|
element_mip.style.gridColumnStart = '4';
 | 
						|
element_mip.style.gridRowStart = '1';
 | 
						|
element_mip.style.gridRowEnd = 'span 3';
 | 
						|
 | 
						|
viewportGrid.appendChild(element1_1);
 | 
						|
viewportGrid.appendChild(element1_2);
 | 
						|
viewportGrid.appendChild(element1_3);
 | 
						|
viewportGrid.appendChild(element2_1);
 | 
						|
viewportGrid.appendChild(element2_2);
 | 
						|
viewportGrid.appendChild(element2_3);
 | 
						|
viewportGrid.appendChild(element3_1);
 | 
						|
viewportGrid.appendChild(element3_2);
 | 
						|
viewportGrid.appendChild(element3_3);
 | 
						|
viewportGrid.appendChild(element_mip);
 | 
						|
 | 
						|
const elements = [
 | 
						|
  element1_1,
 | 
						|
  element1_2,
 | 
						|
  element1_3,
 | 
						|
  element2_1,
 | 
						|
  element2_2,
 | 
						|
  element2_3,
 | 
						|
  element3_1,
 | 
						|
  element3_2,
 | 
						|
  element3_3,
 | 
						|
];
 | 
						|
 | 
						|
elements.forEach((element) => {
 | 
						|
  element.style.width = '100%';
 | 
						|
  element.style.height = '100%';
 | 
						|
 | 
						|
  // Disable right click context menu so we can have right click tools
 | 
						|
  element.oncontextmenu = (e) => e.preventDefault();
 | 
						|
 | 
						|
  resizeObserver.observe(element);
 | 
						|
});
 | 
						|
 | 
						|
element_mip.style.width = '100%';
 | 
						|
element_mip.style.height = '100%';
 | 
						|
element_mip.oncontextmenu = (e) => e.preventDefault();
 | 
						|
resizeObserver.observe(element_mip);
 | 
						|
 | 
						|
const instructions = document.createElement('p');
 | 
						|
 | 
						|
instructions.innerText = `
 | 
						|
  Basic Controls:
 | 
						|
  - Left click: Use selected tool
 | 
						|
  - Middle click: Pan
 | 
						|
  - Right click: Zoom
 | 
						|
  - Mouse Wheel: Stack Scroll
 | 
						|
 | 
						|
  Window Level Tool:
 | 
						|
  - Drag to set the window level for the CT and threshold for the PET.
 | 
						|
 | 
						|
  Crosshairs:
 | 
						|
  - When the tool is active: Click/Drag anywhere in the viewport to move the center of the crosshairs.
 | 
						|
  - Drag a reference line to move it, scrolling the other views.
 | 
						|
  - Square (closest to center): Drag these to change the thickness of the MIP slab in that plane.
 | 
						|
  - Circle (further from center): Drag these to rotate the axes.
 | 
						|
 | 
						|
  PET MIP:
 | 
						|
  - Mouse Wheel: Rotate PET
 | 
						|
  - Left click: Jump all views to the point of highest SUV in the region clicked.
 | 
						|
  
 | 
						|
  Volume_3D Controls:
 | 
						|
  - Middle click : Rotate the image
 | 
						|
  - Mouse Wheel: Rotate PET
 | 
						|
  - Right click : Pan
 | 
						|
  - Left click: Jump all views to the point of highest SUV in the region clicked.
 | 
						|
  `;
 | 
						|
 | 
						|
instructions.style.gridColumnStart = '5';
 | 
						|
instructions.style.gridRowStart = '1';
 | 
						|
instructions.style.gridRowEnd = 'span 3';
 | 
						|
 | 
						|
viewportGrid.append(instructions);
 | 
						|
 | 
						|
// ============================= //
 | 
						|
 | 
						|
const viewportColors = {
 | 
						|
  [viewportIds.CT.AXIAL]: 'rgb(200, 0, 0)',
 | 
						|
  [viewportIds.CT.SAGITTAL]: 'rgb(200, 200, 0)',
 | 
						|
  [viewportIds.CT.CORONAL]: 'rgb(0, 200, 0)',
 | 
						|
  [viewportIds.PT.AXIAL]: 'rgb(200, 0, 0)',
 | 
						|
  [viewportIds.PT.SAGITTAL]: 'rgb(200, 200, 0)',
 | 
						|
  [viewportIds.PT.CORONAL]: 'rgb(0, 200, 0)',
 | 
						|
  [viewportIds.FUSION.AXIAL]: 'rgb(200, 0, 0)',
 | 
						|
  [viewportIds.FUSION.SAGITTAL]: 'rgb(200, 200, 0)',
 | 
						|
  [viewportIds.FUSION.CORONAL]: 'rgb(0, 200, 0)',
 | 
						|
};
 | 
						|
 | 
						|
const viewportReferenceLineControllable = [
 | 
						|
  viewportIds.CT.AXIAL,
 | 
						|
  viewportIds.CT.SAGITTAL,
 | 
						|
  viewportIds.CT.CORONAL,
 | 
						|
  viewportIds.PT.AXIAL,
 | 
						|
  viewportIds.PT.SAGITTAL,
 | 
						|
  viewportIds.PT.CORONAL,
 | 
						|
  viewportIds.FUSION.AXIAL,
 | 
						|
  viewportIds.FUSION.SAGITTAL,
 | 
						|
  viewportIds.FUSION.CORONAL,
 | 
						|
];
 | 
						|
 | 
						|
const viewportReferenceLineDraggableRotatable = [
 | 
						|
  viewportIds.CT.AXIAL,
 | 
						|
  viewportIds.CT.SAGITTAL,
 | 
						|
  viewportIds.CT.CORONAL,
 | 
						|
  viewportIds.PT.AXIAL,
 | 
						|
  viewportIds.PT.SAGITTAL,
 | 
						|
  viewportIds.PT.CORONAL,
 | 
						|
  viewportIds.FUSION.AXIAL,
 | 
						|
  viewportIds.FUSION.SAGITTAL,
 | 
						|
  viewportIds.FUSION.CORONAL,
 | 
						|
];
 | 
						|
 | 
						|
const viewportReferenceLineSlabThicknessControlsOn = [
 | 
						|
  viewportIds.CT.AXIAL,
 | 
						|
  viewportIds.CT.SAGITTAL,
 | 
						|
  viewportIds.CT.CORONAL,
 | 
						|
  viewportIds.PT.AXIAL,
 | 
						|
  viewportIds.PT.SAGITTAL,
 | 
						|
  viewportIds.PT.CORONAL,
 | 
						|
  viewportIds.FUSION.AXIAL,
 | 
						|
  viewportIds.FUSION.SAGITTAL,
 | 
						|
  viewportIds.FUSION.CORONAL,
 | 
						|
];
 | 
						|
 | 
						|
function getReferenceLineColor(viewportId) {
 | 
						|
  return viewportColors[viewportId];
 | 
						|
}
 | 
						|
 | 
						|
function getReferenceLineControllable(viewportId) {
 | 
						|
  const index = viewportReferenceLineControllable.indexOf(viewportId);
 | 
						|
  return index !== -1;
 | 
						|
}
 | 
						|
 | 
						|
function getReferenceLineDraggableRotatable(viewportId) {
 | 
						|
  const index = viewportReferenceLineDraggableRotatable.indexOf(viewportId);
 | 
						|
  return index !== -1;
 | 
						|
}
 | 
						|
 | 
						|
function getReferenceLineSlabThicknessControlsOn(viewportId) {
 | 
						|
  const index =
 | 
						|
    viewportReferenceLineSlabThicknessControlsOn.indexOf(viewportId);
 | 
						|
  return index !== -1;
 | 
						|
}
 | 
						|
 | 
						|
function setUpToolGroups() {
 | 
						|
  // Add tools to Cornerstone3D
 | 
						|
  cornerstoneTools.addTool(WindowLevelTool);
 | 
						|
  cornerstoneTools.addTool(PanTool);
 | 
						|
  cornerstoneTools.addTool(ZoomTool);
 | 
						|
  cornerstoneTools.addTool(StackScrollMouseWheelTool);
 | 
						|
  cornerstoneTools.addTool(MIPJumpToClickTool);
 | 
						|
  cornerstoneTools.addTool(VolumeRotateMouseWheelTool);
 | 
						|
  cornerstoneTools.addTool(CrosshairsTool);
 | 
						|
  cornerstoneTools.addTool(TrackballRotateTool);
 | 
						|
 | 
						|
  // Define tool groups for the main 9 viewports.
 | 
						|
  // Crosshairs currently only supports 3 viewports for a toolgroup due to the
 | 
						|
  // way it is constructed, but its configuration input allows us to synchronize
 | 
						|
  // multiple sets of 3 viewports.
 | 
						|
  const ctToolGroup = ToolGroupManager.createToolGroup(ctToolGroupId);
 | 
						|
  const ptToolGroup = ToolGroupManager.createToolGroup(ptToolGroupId);
 | 
						|
  const fusionToolGroup = ToolGroupManager.createToolGroup(fusionToolGroupId);
 | 
						|
 | 
						|
  ctToolGroup.addViewport(viewportIds.CT.AXIAL, renderingEngineId);
 | 
						|
  ctToolGroup.addViewport(viewportIds.CT.SAGITTAL, renderingEngineId);
 | 
						|
  ctToolGroup.addViewport(viewportIds.CT.CORONAL, renderingEngineId);
 | 
						|
  ptToolGroup.addViewport(viewportIds.PT.AXIAL, renderingEngineId);
 | 
						|
  ptToolGroup.addViewport(viewportIds.PT.SAGITTAL, renderingEngineId);
 | 
						|
  ptToolGroup.addViewport(viewportIds.PT.CORONAL, renderingEngineId);
 | 
						|
  fusionToolGroup.addViewport(viewportIds.FUSION.AXIAL, renderingEngineId);
 | 
						|
  fusionToolGroup.addViewport(viewportIds.FUSION.SAGITTAL, renderingEngineId);
 | 
						|
  fusionToolGroup.addViewport(viewportIds.FUSION.CORONAL, renderingEngineId);
 | 
						|
 | 
						|
  // Manipulation Tools
 | 
						|
  [ctToolGroup, ptToolGroup].forEach((toolGroup) => {
 | 
						|
    toolGroup.addTool(PanTool.toolName);
 | 
						|
    toolGroup.addTool(ZoomTool.toolName);
 | 
						|
    toolGroup.addTool(StackScrollMouseWheelTool.toolName);
 | 
						|
    toolGroup.addTool(CrosshairsTool.toolName, {
 | 
						|
      getReferenceLineColor,
 | 
						|
      getReferenceLineControllable,
 | 
						|
      getReferenceLineDraggableRotatable,
 | 
						|
      getReferenceLineSlabThicknessControlsOn,
 | 
						|
    });
 | 
						|
  });
 | 
						|
 | 
						|
  fusionToolGroup.addTool(PanTool.toolName);
 | 
						|
  fusionToolGroup.addTool(ZoomTool.toolName);
 | 
						|
  fusionToolGroup.addTool(StackScrollMouseWheelTool.toolName);
 | 
						|
  fusionToolGroup.addTool(CrosshairsTool.toolName, {
 | 
						|
    getReferenceLineColor,
 | 
						|
    getReferenceLineControllable,
 | 
						|
    getReferenceLineDraggableRotatable,
 | 
						|
    getReferenceLineSlabThicknessControlsOn,
 | 
						|
    // Only set CT volume to MIP in the fusion viewport
 | 
						|
    filterActorUIDsToSetSlabThickness: [ctVolumeId],
 | 
						|
  });
 | 
						|
 | 
						|
  // Here is the difference in the toolGroups used, that we need to specify the
 | 
						|
  // volume to use for the WindowLevelTool for the fusion viewports
 | 
						|
  ctToolGroup.addTool(WindowLevelTool.toolName);
 | 
						|
  ptToolGroup.addTool(WindowLevelTool.toolName);
 | 
						|
  fusionToolGroup.addTool(WindowLevelTool.toolName, {
 | 
						|
    volumeId: ptVolumeId,
 | 
						|
  });
 | 
						|
 | 
						|
  [ctToolGroup, ptToolGroup, fusionToolGroup].forEach((toolGroup) => {
 | 
						|
    toolGroup.setToolActive(WindowLevelTool.toolName, {
 | 
						|
      bindings: [
 | 
						|
        {
 | 
						|
          mouseButton: MouseBindings.Primary, // Left Click
 | 
						|
        },
 | 
						|
      ],
 | 
						|
    });
 | 
						|
    toolGroup.setToolActive(PanTool.toolName, {
 | 
						|
      bindings: [
 | 
						|
        {
 | 
						|
          mouseButton: MouseBindings.Auxiliary, // Middle Click
 | 
						|
        },
 | 
						|
      ],
 | 
						|
    });
 | 
						|
    toolGroup.setToolActive(ZoomTool.toolName, {
 | 
						|
      bindings: [
 | 
						|
        {
 | 
						|
          mouseButton: MouseBindings.Secondary, // Right Click
 | 
						|
        },
 | 
						|
      ],
 | 
						|
    });
 | 
						|
 | 
						|
    toolGroup.setToolActive(StackScrollMouseWheelTool.toolName);
 | 
						|
    toolGroup.setToolPassive(CrosshairsTool.toolName);
 | 
						|
  });
 | 
						|
 | 
						|
  // MIP Tool Groups
 | 
						|
  mipToolGroup = ToolGroupManager.createToolGroup(mipToolGroupUID);
 | 
						|
  mipToolGroup.addTool('VolumeRotateMouseWheel');
 | 
						|
  mipToolGroup.addTool('MIPJumpToClickTool', {
 | 
						|
    toolGroupId: ptToolGroupId,
 | 
						|
  });
 | 
						|
 | 
						|
  // Set the initial state of the tools, here we set one tool active on left click.
 | 
						|
  // This means left click will draw that tool.
 | 
						|
  mipToolGroup.setToolActive('MIPJumpToClickTool', {
 | 
						|
    bindings: [
 | 
						|
      {
 | 
						|
        mouseButton: MouseBindings.Primary, // Left Click
 | 
						|
      },
 | 
						|
    ],
 | 
						|
  });
 | 
						|
  // As the Stack Scroll mouse wheel is a tool using the `mouseWheelCallback`
 | 
						|
  // hook instead of mouse buttons, it does not need to assign any mouse button.
 | 
						|
  mipToolGroup.setToolActive('VolumeRotateMouseWheel');
 | 
						|
 | 
						|
  mipToolGroup.addViewport(viewportIds.PETMIP.CORONAL, renderingEngineId);
 | 
						|
  console.debug(mipToolGroup);
 | 
						|
}
 | 
						|
 | 
						|
function setUpSynchronizers() {
 | 
						|
  axialCameraPositionSynchronizer = createCameraPositionSynchronizer(
 | 
						|
    axialCameraSynchronizerId
 | 
						|
  );
 | 
						|
  sagittalCameraPositionSynchronizer = createCameraPositionSynchronizer(
 | 
						|
    sagittalCameraSynchronizerId
 | 
						|
  );
 | 
						|
  coronalCameraPositionSynchronizer = createCameraPositionSynchronizer(
 | 
						|
    coronalCameraSynchronizerId
 | 
						|
  );
 | 
						|
  ctVoiSynchronizer = createVOISynchronizer(ctVoiSynchronizerId);
 | 
						|
  ptVoiSynchronizer = createVOISynchronizer(ptVoiSynchronizerId);
 | 
						|
  // Add viewports to camera synchronizers
 | 
						|
  [
 | 
						|
    viewportIds.CT.AXIAL,
 | 
						|
    viewportIds.PT.AXIAL,
 | 
						|
    viewportIds.FUSION.AXIAL,
 | 
						|
  ].forEach((viewportId) => {
 | 
						|
    axialCameraPositionSynchronizer.add({
 | 
						|
      renderingEngineId,
 | 
						|
      viewportId,
 | 
						|
    });
 | 
						|
  });
 | 
						|
  [
 | 
						|
    viewportIds.CT.SAGITTAL,
 | 
						|
    viewportIds.PT.SAGITTAL,
 | 
						|
    viewportIds.FUSION.SAGITTAL,
 | 
						|
  ].forEach((viewportId) => {
 | 
						|
    sagittalCameraPositionSynchronizer.add({
 | 
						|
      renderingEngineId,
 | 
						|
      viewportId,
 | 
						|
    });
 | 
						|
  });
 | 
						|
  [
 | 
						|
    viewportIds.CT.CORONAL,
 | 
						|
    viewportIds.PT.CORONAL,
 | 
						|
    viewportIds.FUSION.CORONAL,
 | 
						|
  ].forEach((viewportId) => {
 | 
						|
    coronalCameraPositionSynchronizer.add({
 | 
						|
      renderingEngineId,
 | 
						|
      viewportId,
 | 
						|
    });
 | 
						|
  });
 | 
						|
 | 
						|
  // Add viewports to VOI synchronizers
 | 
						|
  [
 | 
						|
    viewportIds.CT.AXIAL,
 | 
						|
    viewportIds.CT.SAGITTAL,
 | 
						|
    viewportIds.CT.CORONAL,
 | 
						|
  ].forEach((viewportId) => {
 | 
						|
    ctVoiSynchronizer.add({
 | 
						|
      renderingEngineId,
 | 
						|
      viewportId,
 | 
						|
    });
 | 
						|
  });
 | 
						|
  [
 | 
						|
    viewportIds.FUSION.AXIAL,
 | 
						|
    viewportIds.FUSION.SAGITTAL,
 | 
						|
    viewportIds.FUSION.CORONAL,
 | 
						|
  ].forEach((viewportId) => {
 | 
						|
    // In this example, the fusion viewports are only targets for CT VOI
 | 
						|
    // synchronization, not sources
 | 
						|
    ctVoiSynchronizer.addTarget({
 | 
						|
      renderingEngineId,
 | 
						|
      viewportId,
 | 
						|
    });
 | 
						|
  });
 | 
						|
  [
 | 
						|
    viewportIds.PT.AXIAL,
 | 
						|
    viewportIds.PT.SAGITTAL,
 | 
						|
    viewportIds.PT.CORONAL,
 | 
						|
    viewportIds.FUSION.AXIAL,
 | 
						|
    viewportIds.FUSION.SAGITTAL,
 | 
						|
    viewportIds.FUSION.CORONAL,
 | 
						|
    viewportIds.PETMIP.CORONAL,
 | 
						|
  ].forEach((viewportId) => {
 | 
						|
    ptVoiSynchronizer.add({
 | 
						|
      renderingEngineId,
 | 
						|
      viewportId,
 | 
						|
    });
 | 
						|
  });
 | 
						|
}
 | 
						|
async function getPtImageIds() {
 | 
						|
  return await createImageIdsAndCacheMetaData({
 | 
						|
    StudyInstanceUID,
 | 
						|
    SeriesInstanceUID:
 | 
						|
      '1.3.6.1.4.1.14519.5.2.1.7009.2403.879445243400782656317561081015',
 | 
						|
    wadoRsRoot,
 | 
						|
  });
 | 
						|
}
 | 
						|
async function getCtImageIds() {
 | 
						|
  return await createImageIdsAndCacheMetaData({
 | 
						|
    StudyInstanceUID,
 | 
						|
    SeriesInstanceUID:
 | 
						|
      '1.3.6.1.4.1.14519.5.2.1.7009.2403.226151125820845824875394858561',
 | 
						|
    wadoRsRoot,
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
async function setUpDisplay() {
 | 
						|
  // Create the viewports
 | 
						|
 | 
						|
  const viewportInputArray = [
 | 
						|
    {
 | 
						|
      viewportId: viewportIds.CT.AXIAL,
 | 
						|
      type: ViewportType.ORTHOGRAPHIC,
 | 
						|
      element: element1_1,
 | 
						|
      defaultOptions: {
 | 
						|
        orientation: Enums.OrientationAxis.AXIAL,
 | 
						|
      },
 | 
						|
    },
 | 
						|
    {
 | 
						|
      viewportId: viewportIds.CT.SAGITTAL,
 | 
						|
      type: ViewportType.ORTHOGRAPHIC,
 | 
						|
      element: element1_2,
 | 
						|
      defaultOptions: {
 | 
						|
        orientation: Enums.OrientationAxis.SAGITTAL,
 | 
						|
      },
 | 
						|
    },
 | 
						|
    {
 | 
						|
      viewportId: viewportIds.CT.CORONAL,
 | 
						|
      type: ViewportType.ORTHOGRAPHIC,
 | 
						|
      element: element1_3,
 | 
						|
      defaultOptions: {
 | 
						|
        orientation: Enums.OrientationAxis.CORONAL,
 | 
						|
      },
 | 
						|
    },
 | 
						|
    {
 | 
						|
      viewportId: viewportIds.PT.AXIAL,
 | 
						|
      type: ViewportType.ORTHOGRAPHIC,
 | 
						|
      element: element2_1,
 | 
						|
      defaultOptions: {
 | 
						|
        orientation: Enums.OrientationAxis.AXIAL,
 | 
						|
        background: <Types.Point3>[1, 1, 1],
 | 
						|
      },
 | 
						|
    },
 | 
						|
    {
 | 
						|
      viewportId: viewportIds.PT.SAGITTAL,
 | 
						|
      type: ViewportType.ORTHOGRAPHIC,
 | 
						|
      element: element2_2,
 | 
						|
      defaultOptions: {
 | 
						|
        orientation: Enums.OrientationAxis.SAGITTAL,
 | 
						|
        background: <Types.Point3>[1, 1, 1],
 | 
						|
      },
 | 
						|
    },
 | 
						|
    {
 | 
						|
      viewportId: viewportIds.PT.CORONAL,
 | 
						|
      type: ViewportType.ORTHOGRAPHIC,
 | 
						|
      element: element2_3,
 | 
						|
      defaultOptions: {
 | 
						|
        orientation: Enums.OrientationAxis.CORONAL,
 | 
						|
        background: <Types.Point3>[1, 1, 1],
 | 
						|
      },
 | 
						|
    },
 | 
						|
    {
 | 
						|
      viewportId: viewportIds.FUSION.AXIAL,
 | 
						|
      type: ViewportType.ORTHOGRAPHIC,
 | 
						|
      element: element3_1,
 | 
						|
      defaultOptions: {
 | 
						|
        orientation: Enums.OrientationAxis.AXIAL,
 | 
						|
      },
 | 
						|
    },
 | 
						|
    {
 | 
						|
      viewportId: viewportIds.FUSION.SAGITTAL,
 | 
						|
      type: ViewportType.ORTHOGRAPHIC,
 | 
						|
      element: element3_2,
 | 
						|
      defaultOptions: {
 | 
						|
        orientation: Enums.OrientationAxis.SAGITTAL,
 | 
						|
      },
 | 
						|
    },
 | 
						|
    {
 | 
						|
      viewportId: viewportIds.FUSION.CORONAL,
 | 
						|
      type: ViewportType.ORTHOGRAPHIC,
 | 
						|
      element: element3_3,
 | 
						|
      defaultOptions: {
 | 
						|
        orientation: Enums.OrientationAxis.CORONAL,
 | 
						|
      },
 | 
						|
    },
 | 
						|
    {
 | 
						|
      viewportId: viewportIds.PETMIP.CORONAL,
 | 
						|
      type: ViewportType.ORTHOGRAPHIC,
 | 
						|
      element: element_mip,
 | 
						|
      defaultOptions: {
 | 
						|
        orientation: Enums.OrientationAxis.CORONAL,
 | 
						|
        background: <Types.Point3>[1, 1, 1],
 | 
						|
      },
 | 
						|
    },
 | 
						|
  ];
 | 
						|
 | 
						|
  renderingEngine.setViewports(viewportInputArray);
 | 
						|
 | 
						|
  // Set the volumes to load
 | 
						|
  ptVolume.load();
 | 
						|
  ctVolume.load();
 | 
						|
 | 
						|
  // Set volumes on the viewports
 | 
						|
  await setVolumesForViewports(
 | 
						|
    renderingEngine,
 | 
						|
    [
 | 
						|
      {
 | 
						|
        volumeId: ctVolumeId,
 | 
						|
        callback: setCtTransferFunctionForVolumeActor,
 | 
						|
      },
 | 
						|
    ],
 | 
						|
    [viewportIds.CT.AXIAL, viewportIds.CT.SAGITTAL, viewportIds.CT.CORONAL]
 | 
						|
  );
 | 
						|
 | 
						|
  await setVolumesForViewports(
 | 
						|
    renderingEngine,
 | 
						|
    [
 | 
						|
      {
 | 
						|
        volumeId: ptVolumeId,
 | 
						|
        callback: setPetTransferFunctionForVolumeActor,
 | 
						|
      },
 | 
						|
    ],
 | 
						|
    [viewportIds.PT.AXIAL, viewportIds.PT.SAGITTAL, viewportIds.PT.CORONAL]
 | 
						|
  );
 | 
						|
 | 
						|
  await setVolumesForViewports(
 | 
						|
    renderingEngine,
 | 
						|
    [
 | 
						|
      {
 | 
						|
        volumeId: ctVolumeId,
 | 
						|
        callback: setCtTransferFunctionForVolumeActor,
 | 
						|
      },
 | 
						|
      {
 | 
						|
        volumeId: ptVolumeId,
 | 
						|
        callback: setPetColorMapTransferFunctionForVolumeActor,
 | 
						|
      },
 | 
						|
    ],
 | 
						|
    [
 | 
						|
      viewportIds.FUSION.AXIAL,
 | 
						|
      viewportIds.FUSION.SAGITTAL,
 | 
						|
      viewportIds.FUSION.CORONAL,
 | 
						|
    ]
 | 
						|
  );
 | 
						|
 | 
						|
  // Calculate size of fullBody pet mip
 | 
						|
  const ptVolumeDimensions = ptVolume.dimensions;
 | 
						|
 | 
						|
  // Only make the MIP as large as it needs to be.
 | 
						|
  const slabThickness = Math.sqrt(
 | 
						|
    ptVolumeDimensions[0] * ptVolumeDimensions[0] +
 | 
						|
      ptVolumeDimensions[1] * ptVolumeDimensions[1] +
 | 
						|
      ptVolumeDimensions[2] * ptVolumeDimensions[2]
 | 
						|
  );
 | 
						|
 | 
						|
  setVolumesForViewports(
 | 
						|
    renderingEngine,
 | 
						|
    [
 | 
						|
      {
 | 
						|
        volumeId: ptVolumeId,
 | 
						|
        callback: setPetTransferFunctionForVolumeActor,
 | 
						|
        blendMode: BlendModes.MAXIMUM_INTENSITY_BLEND,
 | 
						|
        slabThickness,
 | 
						|
      },
 | 
						|
    ],
 | 
						|
    [viewportIds.PETMIP.CORONAL]
 | 
						|
  );
 | 
						|
 | 
						|
  initializeCameraSync(renderingEngine);
 | 
						|
 | 
						|
  // Render the viewports
 | 
						|
  renderingEngine.render();
 | 
						|
}
 | 
						|
 | 
						|
addButtonToToolbar({
 | 
						|
  title: 'toggle volume3d',
 | 
						|
  onClick: async () => {
 | 
						|
    const viewportInput = {
 | 
						|
      viewportId: viewportIds.PETMIP.CORONAL,
 | 
						|
      type: ViewportType.VOLUME_3D,
 | 
						|
      element: element_mip,
 | 
						|
      defaultOptions: {
 | 
						|
        orientation: Enums.OrientationAxis.CORONAL,
 | 
						|
        background: <Types.Point3>[1, 1, 1],
 | 
						|
      },
 | 
						|
    };
 | 
						|
    const volume3dToolGroup = mipToolGroup;
 | 
						|
    toggleVolume(viewportInput);
 | 
						|
    setupVolume3dSynchronizer(viewportInput);
 | 
						|
    setUpVolume3dToolGroup(volume3dToolGroup);
 | 
						|
 | 
						|
    renderingEngine.render();
 | 
						|
  },
 | 
						|
});
 | 
						|
 | 
						|
function toggleVolume(viewportInput) {
 | 
						|
  renderingEngine.enableElement(viewportInput);
 | 
						|
 | 
						|
  const viewport = <Types.IVolumeViewport>(
 | 
						|
    renderingEngine.getViewport(viewportIds.PETMIP.CORONAL)
 | 
						|
  );
 | 
						|
 | 
						|
  const ptVolumeDimensions = ptVolume.dimensions;
 | 
						|
 | 
						|
  // Only make the MIP as large as it needs to be.
 | 
						|
  const slabThickness = Math.sqrt(
 | 
						|
    ptVolumeDimensions[0] * ptVolumeDimensions[0] +
 | 
						|
      ptVolumeDimensions[1] * ptVolumeDimensions[1] +
 | 
						|
      ptVolumeDimensions[2] * ptVolumeDimensions[2]
 | 
						|
  );
 | 
						|
 | 
						|
  viewport.setVolumes([
 | 
						|
    {
 | 
						|
      volumeId: ptVolumeId,
 | 
						|
      callback: setPetTransferFunctionForVolumeActor,
 | 
						|
      slabThickness: slabThickness,
 | 
						|
      blendMode: BlendModes.MAXIMUM_INTENSITY_BLEND,
 | 
						|
    },
 | 
						|
  ]);
 | 
						|
}
 | 
						|
function setupVolume3dSynchronizer(viewportInput) {
 | 
						|
  ptVoiSynchronizer.addTarget({
 | 
						|
    renderingEngineId,
 | 
						|
    viewportId: viewportInput.viewportId,
 | 
						|
  });
 | 
						|
}
 | 
						|
function setUpVolume3dToolGroup(toolGroup) {
 | 
						|
  toolGroup.addTool(PanTool.toolName);
 | 
						|
  toolGroup.addTool(TrackballRotateTool.toolName);
 | 
						|
 | 
						|
  toolGroup.setToolActive(PanTool.toolName, {
 | 
						|
    bindings: [
 | 
						|
      {
 | 
						|
        mouseButton: MouseBindings.Secondary,
 | 
						|
      },
 | 
						|
    ],
 | 
						|
  });
 | 
						|
  toolGroup.setToolActive(TrackballRotateTool.toolName, {
 | 
						|
    bindings: [
 | 
						|
      {
 | 
						|
        mouseButton: MouseBindings.Auxiliary,
 | 
						|
      },
 | 
						|
    ],
 | 
						|
  });
 | 
						|
  toolGroup.addViewport(viewportIds.PETMIP.CORONAL, renderingEngineId);
 | 
						|
}
 | 
						|
function initializeCameraSync(renderingEngine) {
 | 
						|
  // The fusion scene is the target as it is scaled to both volumes.
 | 
						|
  // TODO -> We should have a more generic way to do this,
 | 
						|
  // So that when all data is added we can synchronize zoom/position before interaction.
 | 
						|
 | 
						|
  const axialCtViewport = renderingEngine.getViewport(viewportIds.CT.AXIAL);
 | 
						|
  const sagittalCtViewport = renderingEngine.getViewport(
 | 
						|
    viewportIds.CT.SAGITTAL
 | 
						|
  );
 | 
						|
  const coronalCtViewport = renderingEngine.getViewport(viewportIds.CT.CORONAL);
 | 
						|
 | 
						|
  const axialPtViewport = renderingEngine.getViewport(viewportIds.PT.AXIAL);
 | 
						|
  const sagittalPtViewport = renderingEngine.getViewport(
 | 
						|
    viewportIds.PT.SAGITTAL
 | 
						|
  );
 | 
						|
  const coronalPtViewport = renderingEngine.getViewport(viewportIds.PT.CORONAL);
 | 
						|
 | 
						|
  const axialFusionViewport = renderingEngine.getViewport(
 | 
						|
    viewportIds.FUSION.AXIAL
 | 
						|
  );
 | 
						|
  const sagittalFusionViewport = renderingEngine.getViewport(
 | 
						|
    viewportIds.FUSION.SAGITTAL
 | 
						|
  );
 | 
						|
  const coronalFusionViewport = renderingEngine.getViewport(
 | 
						|
    viewportIds.FUSION.CORONAL
 | 
						|
  );
 | 
						|
 | 
						|
  initCameraSynchronization(axialFusionViewport, axialCtViewport);
 | 
						|
  initCameraSynchronization(axialFusionViewport, axialPtViewport);
 | 
						|
 | 
						|
  initCameraSynchronization(sagittalFusionViewport, sagittalCtViewport);
 | 
						|
  initCameraSynchronization(sagittalFusionViewport, sagittalPtViewport);
 | 
						|
 | 
						|
  initCameraSynchronization(coronalFusionViewport, coronalCtViewport);
 | 
						|
  initCameraSynchronization(coronalFusionViewport, coronalPtViewport);
 | 
						|
 | 
						|
  renderingEngine.render();
 | 
						|
}
 | 
						|
 | 
						|
function initCameraSynchronization(sViewport, tViewport) {
 | 
						|
  // Initialise the sync as they viewports will have
 | 
						|
  // Different initial zoom levels for viewports of different sizes.
 | 
						|
 | 
						|
  const camera = sViewport.getCamera();
 | 
						|
 | 
						|
  tViewport.setCamera(camera);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Runs the demo
 | 
						|
 */
 | 
						|
async function run() {
 | 
						|
  // Init Cornerstone and related libraries
 | 
						|
  await initDemo();
 | 
						|
 | 
						|
  // Instantiate a rendering engine
 | 
						|
  renderingEngine = new RenderingEngine(renderingEngineId);
 | 
						|
  // Get Cornerstone imageIds and fetch metadata into RAM
 | 
						|
  ctImageIds = await getCtImageIds();
 | 
						|
 | 
						|
  ptImageIds = await getPtImageIds();
 | 
						|
 | 
						|
  // Define a volume in memory
 | 
						|
  ctVolume = await volumeLoader.createAndCacheVolume(ctVolumeId, {
 | 
						|
    imageIds: ctImageIds,
 | 
						|
  });
 | 
						|
  // Define a volume in memory
 | 
						|
  ptVolume = await volumeLoader.createAndCacheVolume(ptVolumeId, {
 | 
						|
    imageIds: ptImageIds,
 | 
						|
  });
 | 
						|
 | 
						|
  // Display needs to be set up first so that we have viewport to reference for tools and synchronizers.
 | 
						|
  await setUpDisplay();
 | 
						|
 | 
						|
  // Tools and synchronizers can be set up in any order.
 | 
						|
  setUpToolGroups();
 | 
						|
  setUpSynchronizers();
 | 
						|
}
 | 
						|
 | 
						|
run();
 |