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: [1, 1, 1], }, }, { viewportId: viewportIds.PT.SAGITTAL, type: ViewportType.ORTHOGRAPHIC, element: element2_2, defaultOptions: { orientation: Enums.OrientationAxis.SAGITTAL, background: [1, 1, 1], }, }, { viewportId: viewportIds.PT.CORONAL, type: ViewportType.ORTHOGRAPHIC, element: element2_3, defaultOptions: { orientation: Enums.OrientationAxis.CORONAL, background: [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: [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: [1, 1, 1], }, }; const volume3dToolGroup = mipToolGroup; toggleVolume(viewportInput); setupVolume3dSynchronizer(viewportInput); setUpVolume3dToolGroup(volume3dToolGroup); renderingEngine.render(); }, }); function toggleVolume(viewportInput) { renderingEngine.enableElement(viewportInput); const viewport = ( 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();