Open krishnauppili opened 4 months ago
If the orientations are different you need to use the view
synchronization see this example https://www.cornerstonejs.org/live-examples/viewreferencepresentation
Hey @sedghi, thanks for your response! In this example also, when I scroll any view in the top line the bottom line views also gets converted to the same orientation. What I need is when I scroll any view in Sagittal orientation, the other views should be in Axial and Coronal view but must be able to scroll automatically in sync with Sagittal view images I'm scrolling! Is there any default option in cornerstone to do it or do we have to write custom logic for the synchronizer?
Unfortunately, I'm not able to understand what you are saying. Would it be possible for you to draw something or record a video and narrate it?
Describe the Bug
Need to synchronize different volume viewports having different orientation (Sagittal/Axial/Coronal). But now when I click Camera A and B and scroll any one, the other view is also getting changed to the first viewport present.
Steps to Reproduce
import { RenderingEngine, Types, Enums, volumeLoader, setVolumesForViewports, } from '@cornerstonejs/core'; import { initDemo, createImageIdsAndCacheMetaData, setTitleAndDescription, addToggleButtonToToolbar, } from '../../../../utils/demo/helpers'; import * as cornerstoneTools from '@cornerstonejs/tools';
// This is for debugging purposes console.warn( 'Click on index.ts to open source code for this example --------->' );
const { PanTool, WindowLevelTool, ZoomTool, ToolGroupManager, StackScrollMouseWheelTool, Enums: csToolsEnums, synchronizers, SynchronizerManager, } = cornerstoneTools;
const { ViewportType } = Enums; const { MouseBindings } = csToolsEnums;
const { createCameraPositionSynchronizer, createVOISynchronizer } = synchronizers;
// Define a unique id for the volume const volumeName = 'CT_VOLUME_ID'; // Id of the volume less loader prefix const volumeLoaderScheme = 'cornerstoneStreamingImageVolume'; // Loader id which defines which volume loader to use const volumeId =
${volumeLoaderScheme}:${volumeName}
; // VolumeId with loader id + volume idconst cameraSynchronizerId = 'CAMERA_SYNCHRONIZER_ID'; const voiSynchronizerId = 'VOI_SYNCHRONIZER_ID';
const renderingEngineId = 'myRenderingEngine'; const viewportIds = [ 'CT_SAGITTAL_STACK_1', 'CT_AXIAL_STACK_2', 'CT_CORONAL_STACK_3', ];
// ======== Set up page ======== // setTitleAndDescription( 'Synchronization of Volume Viewport Properties', 'Here we demonstrate camera and window/level synchronization across viewports.' );
const size = '500px'; const content = document.getElementById('content'); const viewportGrid = document.createElement('div');
viewportGrid.style.display = 'flex'; viewportGrid.style.display = 'flex'; viewportGrid.style.flexDirection = 'row';
const element1 = document.createElement('div'); const element2 = document.createElement('div'); const element3 = document.createElement('div'); element1.style.width = size; element1.style.height = size; element2.style.width = size; element2.style.height = size; element3.style.width = size; element3.style.height = size;
// Disable right click context menu so we can have right click tools element1.oncontextmenu = (e) => e.preventDefault(); // Disable right click context menu so we can have right click tools element2.oncontextmenu = (e) => e.preventDefault(); // Disable right click context menu so we can have right click tools element3.oncontextmenu = (e) => e.preventDefault();
viewportGrid.appendChild(element1); viewportGrid.appendChild(element2); viewportGrid.appendChild(element3);
content.appendChild(viewportGrid);
const instructions = document.createElement('p'); instructions.innerText = ` Left Click to change window/level Use the mouse wheel to scroll through the stack.
Toggle the controls to add viewports to the synchronization groups. `;
content.append(instructions); // ============================= //
const SynchronizerButtonInfo = [ { viewportLabel: 'A', viewportId: viewportIds[0] }, { viewportLabel: 'B', viewportId: viewportIds[1] }, { viewportLabel: 'C', viewportId: viewportIds[2] }, ];
SynchronizerButtonInfo.forEach(({ viewportLabel, viewportId }) => { addToggleButtonToToolbar({ title:
Camera ${viewportLabel}
, onClick: (toggle) => { const synchronizer = SynchronizerManager.getSynchronizer(cameraSynchronizerId);}); });
SynchronizerButtonInfo.forEach(({ viewportLabel, viewportId }) => { addToggleButtonToToolbar({ title:
VOI ${viewportLabel}
, onClick: (toggle) => { const synchronizer = SynchronizerManager.getSynchronizer(voiSynchronizerId);}); });
/**
Runs the demo */ async function run() { // Init Cornerstone and related libraries await initDemo();
const toolGroupId = 'TOOL_GROUP_ID';
// Add tools to Cornerstone3D cornerstoneTools.addTool(PanTool); cornerstoneTools.addTool(WindowLevelTool); cornerstoneTools.addTool(StackScrollMouseWheelTool); cornerstoneTools.addTool(ZoomTool);
// Define a tool group, which defines how mouse events map to tool commands for // Any viewport using the group const toolGroup = ToolGroupManager.createToolGroup(toolGroupId);
// Add tools to the tool group toolGroup.addTool(WindowLevelTool.toolName, { volumeId }); toolGroup.addTool(PanTool.toolName); toolGroup.addTool(ZoomTool.toolName); toolGroup.addTool(StackScrollMouseWheelTool.toolName);
// Set the initial state of the tools, here all tools are active and bound to // Different mouse inputs 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 }, ], }); // 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. toolGroup.setToolActive(StackScrollMouseWheelTool.toolName);// Create synchronizers createCameraPositionSynchronizer(cameraSynchronizerId); createVOISynchronizer(voiSynchronizerId);
// Get Cornerstone imageIds and fetch metadata into RAM const imageIds = await createImageIdsAndCacheMetaData({ StudyInstanceUID: '1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463', SeriesInstanceUID: '1.3.6.1.4.1.14519.5.2.1.7009.2403.226151125820845824875394858561', wadoRsRoot: 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb', });
// Instantiate a rendering engine const renderingEngine = new RenderingEngine(renderingEngineId);
// Create the viewports const viewportInputArray = [ { viewportId: viewportIds[0], type: ViewportType.ORTHOGRAPHIC, element: element1, defaultOptions: { orientation: Enums.OrientationAxis.SAGITTAL, background:[0.2, 0, 0.2],
},
},
{
viewportId: viewportIds[1],
type: ViewportType.ORTHOGRAPHIC,
element: element2,
defaultOptions: {
orientation: Enums.OrientationAxis.AXIAL,
background: [0.2, 0, 0.2],
},
},
{
viewportId: viewportIds[2],
type: ViewportType.ORTHOGRAPHIC,
element: element3,
defaultOptions: {
orientation: Enums.OrientationAxis.CORONAL,
background: [0.2, 0, 0.2],
},
},
];
renderingEngine.setViewports(viewportInputArray);
// Set the tool group on the viewports viewportIds.forEach((viewportId) => toolGroup.addViewport(viewportId, renderingEngineId) );
// Define a volume in memory const volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds, });
// Set the volume to load volume.load();
setVolumesForViewports(renderingEngine, [{ volumeId }], viewportIds);
// Render the image renderingEngine.renderViewports(viewportIds); }
run();
The current behavior
When Camera A and Camera B are clicked and Viewport 1 is scrolled, Viewport 2 is also changing into Sagittal view.
The expected behavior
Need to retain the orientation such that when camera A and B are clicked and Viewport 1 is scrolled, Viewport 2 should be retained in Axial view but it should be synchronous with Viewport 1.
OS
MacOS
Node version
20
Browser
Chrome
Initial Render
After selecting Camera A and B and scrolling Viewport A
Please assist!