diff --git a/extensions/cornerstone-dicom-rt/src/viewports/OHIFCornerstoneRTViewport.tsx b/extensions/cornerstone-dicom-rt/src/viewports/OHIFCornerstoneRTViewport.tsx index b8e6aa1a5..0b09dc142 100644 --- a/extensions/cornerstone-dicom-rt/src/viewports/OHIFCornerstoneRTViewport.tsx +++ b/extensions/cornerstone-dicom-rt/src/viewports/OHIFCornerstoneRTViewport.tsx @@ -111,7 +111,7 @@ function OHIFCornerstoneRTViewport(props: withAppTypes) { {...props} displaySets={[referencedDisplaySet, rtDisplaySet]} viewportOptions={{ - viewportType: 'volume', + viewportType: 'stack', toolGroupId: toolGroupId, orientation: viewportOptions.orientation, viewportId: viewportOptions.viewportId, diff --git a/extensions/cornerstone-dicom-seg/src/commandsModule.ts b/extensions/cornerstone-dicom-seg/src/commandsModule.ts index fa6361bb8..aa104bc51 100644 --- a/extensions/cornerstone-dicom-seg/src/commandsModule.ts +++ b/extensions/cornerstone-dicom-seg/src/commandsModule.ts @@ -213,7 +213,7 @@ const commandsModule = ({ updateViewportsForSegmentationRendering({ viewportId, servicesManager, - referencedDisplaySetInstanceUID: displaySet.referencedDisplaySetInstanceUID, + displaySet, loadFn: async () => { const segDisplaySet = displaySet; const suppressEvents = false; diff --git a/extensions/cornerstone-dicom-seg/src/utils/hydrationUtils.ts b/extensions/cornerstone-dicom-seg/src/utils/hydrationUtils.ts index 9e292ba98..2fd6114db 100644 --- a/extensions/cornerstone-dicom-seg/src/utils/hydrationUtils.ts +++ b/extensions/cornerstone-dicom-seg/src/utils/hydrationUtils.ts @@ -10,7 +10,7 @@ import { Enums, cache } from '@cornerstonejs/core'; * @param params.viewportId - ID of the viewport to be updated. * @param params.loadFn - Function to load the segmentation data. * @param params.servicesManager - The services manager. - * @param params.referencedDisplaySetInstanceUID - Optional UID for the referenced display set instance. + * @param params.displaySet - the display set. * * @returns Returns true upon successful update of viewports for segmentation rendering. */ @@ -18,12 +18,12 @@ async function updateViewportsForSegmentationRendering({ viewportId, loadFn, servicesManager, - referencedDisplaySetInstanceUID, + displaySet, }: { viewportId: string; loadFn: () => Promise; servicesManager: AppTypes.ServicesManager; - referencedDisplaySetInstanceUID?: string; + displaySet?: any; }) { const { cornerstoneViewportService, segmentationService, viewportGridService } = servicesManager.services; @@ -31,13 +31,13 @@ async function updateViewportsForSegmentationRendering({ const viewport = getTargetViewport({ viewportId, viewportGridService }); const targetViewportId = viewport.viewportOptions.viewportId; - referencedDisplaySetInstanceUID = - referencedDisplaySetInstanceUID || viewport?.displaySetInstanceUIDs[0]; + const referencedDisplaySetInstanceUID = + displaySet?.referencedDisplaySetInstanceUID || viewport?.displaySetInstanceUIDs[0]; const updatedViewports = getUpdatedViewportsForSegmentation({ servicesManager, viewportId, - referencedDisplaySetInstanceUID, + displaySet, }); // create Segmentation callback which needs to be waited until @@ -56,7 +56,7 @@ async function updateViewportsForSegmentationRendering({ updatedViewports.forEach(async viewport => { viewport.viewportOptions = { ...viewport.viewportOptions, - viewportType: 'volume', + viewportType: displaySet?.Modality === 'RTSTRUCT' ? 'stack' : 'volume', needsRerendering: true, }; const viewportId = viewport.viewportId; @@ -66,7 +66,7 @@ async function updateViewportsForSegmentationRendering({ // only run the createSegmentationForVolume for the targetViewportId // since the rest will get handled by cornerstoneViewportService - if (volumeExists && viewportId === targetViewportId) { + if ((volumeExists || displaySet.Modality === 'RTSTRUCT') && viewportId === targetViewportId) { await createSegmentationForVolume(); return; } @@ -126,14 +126,14 @@ const getTargetViewport = ({ viewportId, viewportGridService }) => { * @param {Object} params - Parameters for the function. * @param params.viewportId - the ID of the viewport to be updated. * @param params.servicesManager - The services manager - * @param params.referencedDisplaySetInstanceUID - Optional UID for the referenced display set instance. + * @param params.displaySet - the display set. * * @returns {Array} Returns an array of viewports that require updates for segmentation rendering. */ function getUpdatedViewportsForSegmentation({ viewportId, servicesManager, - referencedDisplaySetInstanceUID, + displaySet, }: withAppTypes) { const { hangingProtocolService, displaySetService, segmentationService, viewportGridService } = servicesManager.services; @@ -146,7 +146,7 @@ function getUpdatedViewportsForSegmentation({ const displaySetInstanceUIDs = viewports.get(targetViewportId).displaySetInstanceUIDs; const referenceDisplaySetInstanceUID = - referencedDisplaySetInstanceUID || displaySetInstanceUIDs[0]; + displaySet?.referencedDisplaySetInstanceUID || displaySetInstanceUIDs[0]; const referencedDisplaySet = displaySetService.getDisplaySetByUID(referenceDisplaySetInstanceUID); const segmentationFrameOfReferenceUID = referencedDisplaySet.instances[0].FrameOfReferenceUID; @@ -175,7 +175,7 @@ function getUpdatedViewportsForSegmentation({ viewportId, displaySetInstanceUIDs: viewport.displaySetInstanceUIDs, viewportOptions: { - viewportType: 'volume', + viewportType: displaySet.Modality === 'RTSTRUCT' ? 'stack' : 'volume', needsRerendering: true, }, }); diff --git a/extensions/cornerstone/src/Viewport/Overlays/CustomizableViewportOverlay.tsx b/extensions/cornerstone/src/Viewport/Overlays/CustomizableViewportOverlay.tsx index 2c6ff2749..c36190e30 100644 --- a/extensions/cornerstone/src/Viewport/Overlays/CustomizableViewportOverlay.tsx +++ b/extensions/cornerstone/src/Viewport/Overlays/CustomizableViewportOverlay.tsx @@ -317,7 +317,7 @@ function CustomizableViewportOverlay({ function _getViewportInstances(viewportData) { const imageIds = []; if (viewportData.viewportType === Enums.ViewportType.STACK) { - imageIds.push(viewportData.data.imageIds[0]); + imageIds.push(viewportData.data[0].imageIds[0]); } else if (viewportData.viewportType === Enums.ViewportType.ORTHOGRAPHIC) { const volumes = viewportData.data; volumes.forEach(volume => { @@ -357,7 +357,7 @@ const getInstanceNumber = (viewportData, viewportId, imageIndex, cornerstoneView }; function _getInstanceNumberFromStack(viewportData, imageIndex) { - const imageIds = viewportData.data.imageIds; + const imageIds = viewportData.data[0].imageIds; const imageId = imageIds[imageIndex]; if (!imageId) { diff --git a/extensions/cornerstone/src/Viewport/Overlays/ViewportImageScrollbar.tsx b/extensions/cornerstone/src/Viewport/Overlays/ViewportImageScrollbar.tsx index 88ab4e4ae..4de754552 100644 --- a/extensions/cornerstone/src/Viewport/Overlays/ViewportImageScrollbar.tsx +++ b/extensions/cornerstone/src/Viewport/Overlays/ViewportImageScrollbar.tsx @@ -48,7 +48,7 @@ function CornerstoneImageScrollbar({ setImageSliceData({ imageIndex: imageIndex, - numberOfSlices: viewportData.data.imageIds.length, + numberOfSlices: viewportData.data[0].imageIds.length, }); return; @@ -78,7 +78,7 @@ function CornerstoneImageScrollbar({ // find the index of imageId in the imageIds setImageSliceData({ imageIndex: newImageIdIndex, - numberOfSlices: viewportData.data.imageIds.length, + numberOfSlices: viewportData.data[0].imageIds.length, }); }; diff --git a/extensions/cornerstone/src/Viewport/Overlays/ViewportOrientationMarkers.tsx b/extensions/cornerstone/src/Viewport/Overlays/ViewportOrientationMarkers.tsx index eb1dd8200..4b02a3eaa 100644 --- a/extensions/cornerstone/src/Viewport/Overlays/ViewportOrientationMarkers.tsx +++ b/extensions/cornerstone/src/Viewport/Overlays/ViewportOrientationMarkers.tsx @@ -67,7 +67,7 @@ function ViewportOrientationMarkers({ let rowCosines, columnCosines; if (viewportData.viewportType === 'stack') { const imageIndex = imageSliceData.imageIndex; - const imageId = viewportData.data.imageIds?.[imageIndex]; + const imageId = viewportData.data[0].imageIds?.[imageIndex]; // Workaround for below TODO stub if (!imageId) { diff --git a/extensions/cornerstone/src/services/CornerstoneCacheService/CornerstoneCacheService.ts b/extensions/cornerstone/src/services/CornerstoneCacheService/CornerstoneCacheService.ts index b02cc43f1..245c4964e 100644 --- a/extensions/cornerstone/src/services/CornerstoneCacheService/CornerstoneCacheService.ts +++ b/extensions/cornerstone/src/services/CornerstoneCacheService/CornerstoneCacheService.ts @@ -129,14 +129,25 @@ class CornerstoneCacheService { return newViewportData; } - private _getStackViewportData( + private async _getStackViewportData( dataSource, displaySets, initialImageIndex, viewportType: Enums.ViewportType - ): StackViewportData { - // For Stack Viewport we don't have fusion currently - const displaySet = displaySets[0]; + ): Promise { + const overlayDisplaySets = displaySets.filter(ds => ds.isOverlayDisplaySet); + const nonOverlayDisplaySets = displaySets.filter(ds => !ds.isOverlayDisplaySet); + + // load overlays if they are not loaded + for (const overlayDisplaySet of overlayDisplaySets) { + if (overlayDisplaySet.load && overlayDisplaySet.load instanceof Function) { + const { userAuthenticationService } = this.servicesManager.services; + const headers = userAuthenticationService.getAuthorizationHeader(); + await overlayDisplaySet.load({ headers }); + } + } + + const displaySet = nonOverlayDisplaySets[0]; let stackImageIds = this.stackImageIds.get(displaySet.displaySetInstanceUID); @@ -145,23 +156,23 @@ class CornerstoneCacheService { this.stackImageIds.set(displaySet.displaySetInstanceUID, stackImageIds); } - const { displaySetInstanceUID, StudyInstanceUID, isCompositeStack } = displaySet; + // Ensuring the first non-overlay `displaySet` is always the primary one + const StackViewportData = [displaySet, ...overlayDisplaySets].map(ds => { + const { displaySetInstanceUID, StudyInstanceUID, isCompositeStack } = ds; - const StackViewportData: StackViewportData = { - viewportType, - data: { + return { StudyInstanceUID, displaySetInstanceUID, isCompositeStack, imageIds: stackImageIds, - }, + initialImageIndex, + }; + }); + + return { + viewportType, + data: StackViewportData, }; - - if (typeof initialImageIndex === 'number') { - StackViewportData.data.initialImageIndex = initialImageIndex; - } - - return StackViewportData; } private async _getVolumeViewportData( diff --git a/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts b/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts index b59b11d90..35c9d3e15 100644 --- a/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts +++ b/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts @@ -557,9 +557,14 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi ): Promise { const displaySetOptions = viewportInfo.getDisplaySetOptions(); - const { imageIds, initialImageIndex, displaySetInstanceUID } = viewportData.data; + const displaySetInstanceUIDs = viewportData.data.map(data => data.displaySetInstanceUID); - this.viewportsDisplaySets.set(viewport.id, [displaySetInstanceUID]); + // based on the cache service construct always the first one is the non-overlay + // and the rest are overlays + + this.viewportsDisplaySets.set(viewport.id, [...displaySetInstanceUIDs]); + + const { initialImageIndex, imageIds } = viewportData.data[0]; let initialImageIndexToUse = presentations?.positionPresentation?.initialImageIndex ?? initialImageIndex; @@ -588,6 +593,8 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi } } + this._handleOverlays(viewport); + return viewport.setStack(imageIds, initialImageIndexToUse).then(() => { viewport.setProperties({ ...properties }); this.setPresentations(viewport.id, presentations); @@ -772,24 +779,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi this.setPresentations(viewport.id, presentations); - // load any secondary displaySets - const displaySetInstanceUIDs = this.viewportsDisplaySets.get(viewport.id); - - // can be SEG or RTSTRUCT for now - const overlayDisplaySet = displaySetInstanceUIDs - .map(displaySetService.getDisplaySetByUID) - .find(displaySet => displaySet?.isOverlayDisplaySet); - - if (overlayDisplaySet) { - this.addOverlayRepresentationForDisplaySet(overlayDisplaySet, viewport); - } else { - // If the displaySet is not a SEG displaySet we assume it is a primary displaySet - // and we can look into hydrated segmentations to check if any of them are - // associated with the primary displaySet - - // get segmentations only returns the hydrated segmentations - this._addSegmentationRepresentationToToolGroupIfNecessary(displaySetInstanceUIDs, viewport); - } + this._handleOverlays(viewport); const toolGroup = toolGroupService.getToolGroupForViewport(viewport.id); csToolsUtils.segmentation.triggerSegmentationRender(toolGroup.id); @@ -809,6 +799,28 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi }); } + private _handleOverlays(viewport: Types.IStackViewport | Types.IVolumeViewport) { + const { displaySetService } = this.servicesManager.services; + + // load any secondary displaySets + const displaySetInstanceUIDs = this.viewportsDisplaySets.get(viewport.id); + + // can be SEG or RTSTRUCT for now + const overlayDisplaySet = displaySetInstanceUIDs + .map(displaySetService.getDisplaySetByUID) + .find(displaySet => displaySet?.isOverlayDisplaySet); + if (overlayDisplaySet) { + this.addOverlayRepresentationForDisplaySet(overlayDisplaySet, viewport); + } else { + // If the displaySet is not a SEG displaySet we assume it is a primary displaySet + // and we can look into hydrated segmentations to check if any of them are + // associated with the primary displaySet + + // get segmentations only returns the hydrated segmentations + this._addSegmentationRepresentationToToolGroupIfNecessary(displaySetInstanceUIDs, viewport); + } + } + private _addSegmentationRepresentationToToolGroupIfNecessary( displaySetInstanceUIDs: string[], viewport: any diff --git a/extensions/cornerstone/src/types/CornerstoneCacheService.ts b/extensions/cornerstone/src/types/CornerstoneCacheService.ts index ba79745a0..3fef228b2 100644 --- a/extensions/cornerstone/src/types/CornerstoneCacheService.ts +++ b/extensions/cornerstone/src/types/CornerstoneCacheService.ts @@ -22,7 +22,7 @@ type VolumeData = { type StackViewportData = { viewportType: Enums.ViewportType; - data: StackData; + data: StackData[]; }; type VolumeViewportData = {