From 6f0fe62206f4aa1f965c53e2be99f901561ecc5d Mon Sep 17 00:00:00 2001 From: Ouwen Huang Date: Wed, 3 May 2023 06:20:17 +0000 Subject: [PATCH] feat: added promise lifecycle to Viewport --- .../src/Viewport/OHIFCornerstoneViewport.tsx | 8 +- .../CornerstoneViewportService.ts | 99 +++++++++---------- platform/core/src/types/HangingProtocol.ts | 7 ++ .../viewer/src/components/ViewportGrid.tsx | 13 +++ 4 files changed, 75 insertions(+), 52 deletions(-) diff --git a/extensions/cornerstone/src/Viewport/OHIFCornerstoneViewport.tsx b/extensions/cornerstone/src/Viewport/OHIFCornerstoneViewport.tsx index a55d74edde2..c14d6e207b1 100644 --- a/extensions/cornerstone/src/Viewport/OHIFCornerstoneViewport.tsx +++ b/extensions/cornerstone/src/Viewport/OHIFCornerstoneViewport.tsx @@ -119,6 +119,7 @@ const OHIFCornerstoneViewport = React.memo(props => { servicesManager, onElementEnabled, onElementDisabled, + onViewportDataLoad, // Note: you SHOULD NOT use the initialImageIdOrIndex for manipulation // of the imageData in the OHIFCornerstoneViewport. This prop is used // to set the initial state of the viewport's first image to render @@ -433,7 +434,11 @@ const OHIFCornerstoneViewport = React.memo(props => { viewportOptions, displaySetOptions, presentations - ); + ).then((val) => { + if (onViewportDataLoad) { + onViewportDataLoad(val) + } + });; if (measurement) { cs3DTools.annotation.selection.setAnnotationSelected(measurement.uid); @@ -723,6 +728,7 @@ OHIFCornerstoneViewport.propTypes = { displaySetOptions: PropTypes.arrayOf(PropTypes.any), servicesManager: PropTypes.object.isRequired, onElementEnabled: PropTypes.func, + onViewportDataLoad: PropTypes.func, // Note: you SHOULD NOT use the initialImageIdOrIndex for manipulation // of the imageData in the OHIFCornerstoneViewport. This prop is used // to set the initial state of the viewport's first image to render diff --git a/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts b/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts index 012c69e6afd..3bfb36ead48 100644 --- a/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts +++ b/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts @@ -46,8 +46,10 @@ const EVENTS = { * Handles cornerstone viewport logic including enabling, disabling, and * updating the viewport. */ -class CornerstoneViewportService extends PubSubService - implements IViewportService { +class CornerstoneViewportService + extends PubSubService + implements IViewportService +{ static REGISTRATION = { name: 'cornerstoneViewportService', altName: 'CornerstoneViewportService', @@ -238,7 +240,7 @@ class CornerstoneViewportService extends PubSubService publicViewportOptions: PublicViewportOptions, publicDisplaySetOptions: DisplaySetOptions[], presentations?: Presentations - ): void { + ): Promise { const renderingEngine = this.getRenderingEngine(); const viewportId = publicViewportOptions.viewportId || this.getViewportId(viewportIndex); @@ -261,14 +263,12 @@ class CornerstoneViewportService extends PubSubService viewportInfo.setRenderingEngineId(renderingEngine.id); - const { - viewportOptions, - displaySetOptions, - } = this._getViewportAndDisplaySetOptions( - publicViewportOptions, - publicDisplaySetOptions, - viewportInfo - ); + const { viewportOptions, displaySetOptions } = + this._getViewportAndDisplaySetOptions( + publicViewportOptions, + publicDisplaySetOptions, + viewportInfo + ); viewportInfo.setViewportOptions(viewportOptions); viewportInfo.setDisplaySetOptions(displaySetOptions); @@ -296,7 +296,12 @@ class CornerstoneViewportService extends PubSubService renderingEngine.enableElement(viewportInput); const viewport = renderingEngine.getViewport(viewportId); - this._setDisplaySets(viewport, viewportData, viewportInfo, presentations); + const displaySetPromsie = this._setDisplaySets( + viewport, + viewportData, + viewportInfo, + presentations + ); // The broadcast event here ensures that listeners have a valid, up to date // viewport to access. Doing it too early can result in exceptions or @@ -306,6 +311,8 @@ class CornerstoneViewportService extends PubSubService viewportIndex, viewportId, }); + + return displaySetPromsie; } public getCornerstoneViewport( @@ -370,14 +377,11 @@ class CornerstoneViewportService extends PubSubService viewportData: StackViewportData, viewportInfo: ViewportInfo, presentations: Presentations - ): void { + ): Promise { const displaySetOptions = viewportInfo.getDisplaySetOptions(); - const { - imageIds, - initialImageIndex, - displaySetInstanceUID, - } = viewportData.data; + const { imageIds, initialImageIndex, displaySetInstanceUID } = + viewportData.data; this.viewportsDisplaySets.set(viewport.id, [displaySetInstanceUID]); @@ -409,7 +413,7 @@ class CornerstoneViewportService extends PubSubService } } - viewport.setStack(imageIds, initialImageIndexToUse).then(() => { + return viewport.setStack(imageIds, initialImageIndexToUse).then(() => { viewport.setProperties(properties); const camera = presentations.positionPresentation?.camera; if (camera) viewport.setCamera(camera); @@ -436,9 +440,8 @@ class CornerstoneViewportService extends PubSubService const viewport = this.getCornerstoneViewport( viewportInfo.getViewportId() ); - const imageSliceData = csUtils.getImageSliceDataForVolumeViewport( - viewport - ); + const imageSliceData = + csUtils.getImageSliceDataForVolumeViewport(viewport); if (!imageSliceData) { return; @@ -571,11 +574,9 @@ class CornerstoneViewportService extends PubSubService viewport, volumeInputArray, presentations - ) { - const { - displaySetService, - toolGroupService, - } = this.servicesManager.services; + ): Promise { + const { displaySetService, toolGroupService } = + this.servicesManager.services; await viewport.setVolumes(volumeInputArray); this.setPresentations(viewport, presentations); @@ -621,10 +622,8 @@ class CornerstoneViewportService extends PubSubService displaySetInstanceUIDs: string[], viewport: any ) { - const { - segmentationService, - toolGroupService, - } = this.servicesManager.services; + const { segmentationService, toolGroupService } = + this.servicesManager.services; const toolGroup = toolGroupService.getToolGroupForViewport(viewport.id); @@ -639,9 +638,10 @@ class CornerstoneViewportService extends PubSubService // if there is already a segmentation representation for this segmentation // for this toolGroup, don't bother at all - const isSegmentationInToolGroup = toolGroupSegmentationRepresentations.find( - representation => representation.segmentationId === segmentation.id - ); + const isSegmentationInToolGroup = + toolGroupSegmentationRepresentations.find( + representation => representation.segmentationId === segmentation.id + ); if (isSegmentationInToolGroup) { continue; @@ -684,10 +684,8 @@ class CornerstoneViewportService extends PubSubService displaySet: any, viewport: any ) { - const { - segmentationService, - toolGroupService, - } = this.servicesManager.services; + const { segmentationService, toolGroupService } = + this.servicesManager.services; const { referencedVolumeId } = displaySet; const segmentationId = displaySet.displaySetInstanceUID; @@ -713,7 +711,7 @@ class CornerstoneViewportService extends PubSubService viewportIndex: number, viewportData, keepCamera = false - ) { + ): Promise { const viewportInfo = this.getViewportInfoByIndex(viewportIndex); const viewportId = viewportInfo.getViewportId(); @@ -721,19 +719,18 @@ class CornerstoneViewportService extends PubSubService const viewportCamera = viewport.getCamera(); if (viewport instanceof VolumeViewport) { - this._setVolumeViewport(viewport, viewportData, viewportInfo).then(() => { - if (keepCamera) { - viewport.setCamera(viewportCamera); - viewport.render(); + return this._setVolumeViewport(viewport, viewportData, viewportInfo).then( + () => { + if (keepCamera) { + viewport.setCamera(viewportCamera); + viewport.render(); + } } - }); - - return; + ); } if (viewport instanceof StackViewport) { - this._setStackViewport(viewport, viewportData, viewportInfo); - return; + return this._setStackViewport(viewport, viewportData, viewportInfo); } } @@ -786,9 +783,9 @@ class CornerstoneViewportService extends PubSubService viewportData: StackViewportData | VolumeViewportData, viewportInfo: ViewportInfo, presentations: Presentations = {} - ): void { + ): Promise { if (viewport instanceof StackViewport) { - this._setStackViewport( + return this._setStackViewport( viewport, viewportData as StackViewportData, viewportInfo, @@ -798,7 +795,7 @@ class CornerstoneViewportService extends PubSubService viewport instanceof VolumeViewport || viewport instanceof VolumeViewport3D ) { - this._setVolumeViewport( + return this._setVolumeViewport( viewport, viewportData as VolumeViewportData, viewportInfo, diff --git a/platform/core/src/types/HangingProtocol.ts b/platform/core/src/types/HangingProtocol.ts index fec29f4896e..a80c4c7130d 100644 --- a/platform/core/src/types/HangingProtocol.ts +++ b/platform/core/src/types/HangingProtocol.ts @@ -250,6 +250,13 @@ export type ProtocolNotifications = { // The numRows and numCols is included in the command params, so it is possible // to apply a specific hanging protocol onLayoutChange?: Command[]; + + // Once all image data containers are loaded, this command runs. + // While the data container exists, the actual internal data may not be + // hydrated. This is because stack and volume viewports will load progressively. + // TODO: Another protocol notification which may need to load is when all data is actually + // loaded. This may be related to imageLoader code. + onLayoutViewportDataContainersLoaded?: Command[]; }; /** diff --git a/platform/viewer/src/components/ViewportGrid.tsx b/platform/viewer/src/components/ViewportGrid.tsx index dea53511c9c..dd6da080735 100644 --- a/platform/viewer/src/components/ViewportGrid.tsx +++ b/platform/viewer/src/components/ViewportGrid.tsx @@ -5,6 +5,7 @@ import { ViewportGrid, ViewportPane, useViewportGrid } from '@ohif/ui'; import { utils } from '@ohif/core'; import EmptyViewport from './EmptyViewport'; import classNames from 'classnames'; +import { commandsManager } from '../App'; const { isEqualWithin } = utils; @@ -260,6 +261,17 @@ function ViewerViewportGrid(props) { const viewportPanes = []; const numViewportPanes = viewportGridService.getNumViewportPanes(); + let readyViewports = 0; + const checkReady = () => { + readyViewports = readyViewports + 1; + if (readyViewports === numViewportPanes) { + const layoutLoaded = hangingProtocolService?.protocol?.callbacks?.onLayoutViewportDataContainersLoaded; + if (layoutLoaded) { + commandsManager.run(layoutLoaded) + } + } + } + for (let i = 0; i < numViewportPanes; i++) { const viewportIndex = i; const isActive = activeViewportIndex === viewportIndex; @@ -344,6 +356,7 @@ function ViewerViewportGrid(props) { viewportOptions={viewportOptions} displaySetOptions={displaySetOptions} needsRerendering={displaySetsNeedsRerendering} + onViewportDataLoad={checkReady} />