From d250d04580883446fcb8d748b2a97c5c198922af Mon Sep 17 00:00:00 2001 From: Alireza Date: Thu, 18 Apr 2024 23:21:02 -0400 Subject: [PATCH] fix(layouts): and fix thumbnail in touch and update migration guide for 3.8 release (#4052) --- extensions/cornerstone-dicom-seg/package.json | 4 +- .../src/utils/hydrationUtils.ts | 5 +- extensions/cornerstone-dicom-sr/package.json | 6 +- .../cornerstone-dynamic-volume/package.json | 6 +- extensions/cornerstone/package.json | 10 +-- extensions/cornerstone/src/hps/mpr.ts | 12 +-- .../cornerstone/src/initCornerstoneTools.js | 1 + .../default/src/Panels/PanelStudyBrowser.tsx | 6 +- .../src/Toolbar/ToolbarLayoutSelector.tsx | 4 + extensions/default/src/commandsModule.ts | 12 ++- .../default/src/findViewportsByPosition.ts | 10 ++- .../default/src/getSopClassHandlerModule.js | 6 +- extensions/measurement-tracking/package.json | 4 +- .../PanelStudyBrowserTracking.tsx | 6 +- modes/basic-test-mode/src/moreTools.ts | 8 +- modes/longitudinal/src/moreTools.ts | 8 +- modes/segmentation/src/toolbarButtons.ts | 8 +- .../study-list/OHIFStudyList.spec.js | 15 +++- platform/app/package.json | 2 +- platform/app/src/App.tsx | 7 ++ platform/app/src/components/ViewportGrid.tsx | 12 +-- platform/core/package.json | 2 +- .../HangingProtocolService.ts | 46 +++++++--- .../src/utils/isDisplaySetReconstructable.js | 10 ++- .../docs/migration-guide/from-3p7-to-3p8.md | 5 ++ .../services/data/StateSyncService.md | 1 - .../ui/src/components/Thumbnail/Thumbnail.tsx | 19 +++- .../ThumbnailNoImage/ThumbnailNoImage.tsx | 16 +++- .../contextProviders/ViewportGridProvider.tsx | 27 +++--- yarn.lock | 89 ++++++------------- 30 files changed, 209 insertions(+), 158 deletions(-) diff --git a/extensions/cornerstone-dicom-seg/package.json b/extensions/cornerstone-dicom-seg/package.json index 88eee447c..982eb1868 100644 --- a/extensions/cornerstone-dicom-seg/package.json +++ b/extensions/cornerstone-dicom-seg/package.json @@ -46,8 +46,8 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^1.70.8", - "@cornerstonejs/core": "^1.70.8", + "@cornerstonejs/adapters": "^1.70.9", + "@cornerstonejs/core": "^1.70.9", "@kitware/vtk.js": "30.3.3", "react-color": "^2.19.3" } diff --git a/extensions/cornerstone-dicom-seg/src/utils/hydrationUtils.ts b/extensions/cornerstone-dicom-seg/src/utils/hydrationUtils.ts index ad5898ee9..8726d9dc4 100644 --- a/extensions/cornerstone-dicom-seg/src/utils/hydrationUtils.ts +++ b/extensions/cornerstone-dicom-seg/src/utils/hydrationUtils.ts @@ -138,7 +138,7 @@ function getUpdatedViewportsForSegmentation({ const { hangingProtocolService, displaySetService, segmentationService, viewportGridService } = servicesManager.services; - const { viewports } = viewportGridService.getState(); + const { viewports, isHangingProtocolLayout } = viewportGridService.getState(); const viewport = getTargetViewport({ viewportId, viewportGridService }); const targetViewportId = viewport.viewportOptions.viewportId; @@ -153,7 +153,8 @@ function getUpdatedViewportsForSegmentation({ const updatedViewports = hangingProtocolService.getViewportsRequireUpdate( targetViewportId, - referenceDisplaySetInstanceUID + referenceDisplaySetInstanceUID, + isHangingProtocolLayout ); viewports.forEach((viewport, viewportId) => { diff --git a/extensions/cornerstone-dicom-sr/package.json b/extensions/cornerstone-dicom-sr/package.json index a6fbd908c..f325b2604 100644 --- a/extensions/cornerstone-dicom-sr/package.json +++ b/extensions/cornerstone-dicom-sr/package.json @@ -46,9 +46,9 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^1.70.8", - "@cornerstonejs/core": "^1.70.8", - "@cornerstonejs/tools": "^1.70.8", + "@cornerstonejs/adapters": "^1.70.9", + "@cornerstonejs/core": "^1.70.9", + "@cornerstonejs/tools": "^1.70.9", "classnames": "^2.3.2" } } diff --git a/extensions/cornerstone-dynamic-volume/package.json b/extensions/cornerstone-dynamic-volume/package.json index b0e6b1764..46ea7b748 100644 --- a/extensions/cornerstone-dynamic-volume/package.json +++ b/extensions/cornerstone-dynamic-volume/package.json @@ -42,9 +42,9 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/core": "^1.70.8", - "@cornerstonejs/streaming-image-volume-loader": "^1.70.8", - "@cornerstonejs/tools": "^1.70.8", + "@cornerstonejs/core": "^1.70.9", + "@cornerstonejs/streaming-image-volume-loader": "^1.70.9", + "@cornerstonejs/tools": "^1.70.9", "classnames": "^2.3.2" } } diff --git a/extensions/cornerstone/package.json b/extensions/cornerstone/package.json index db2c5cfc0..db6cca3e8 100644 --- a/extensions/cornerstone/package.json +++ b/extensions/cornerstone/package.json @@ -38,7 +38,7 @@ "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2", "@cornerstonejs/codec-openjph": "^2.4.2", - "@cornerstonejs/dicom-image-loader": "^1.70.8", + "@cornerstonejs/dicom-image-loader": "^1.70.9", "@icr/polyseg-wasm": "^0.4.0", "@ohif/core": "3.8.0-beta.85", "@ohif/ui": "3.8.0-beta.85", @@ -55,10 +55,10 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^1.70.8", - "@cornerstonejs/core": "^1.70.8", - "@cornerstonejs/streaming-image-volume-loader": "^1.70.8", - "@cornerstonejs/tools": "^1.70.8", + "@cornerstonejs/adapters": "^1.70.9", + "@cornerstonejs/core": "^1.70.9", + "@cornerstonejs/streaming-image-volume-loader": "^1.70.9", + "@cornerstonejs/tools": "^1.70.9", "@icr/polyseg-wasm": "^0.4.0", "@kitware/vtk.js": "30.3.3", "html2canvas": "^1.4.1", diff --git a/extensions/cornerstone/src/hps/mpr.ts b/extensions/cornerstone/src/hps/mpr.ts index ea2219163..fa54ffde6 100644 --- a/extensions/cornerstone/src/hps/mpr.ts +++ b/extensions/cornerstone/src/hps/mpr.ts @@ -14,17 +14,7 @@ export const mpr: Types.HangingProtocol.Protocol = { numberOfPriorsReferenced: 0, protocolMatchingRules: [], imageLoadStrategy: 'nth', - callbacks: { - // Switches out of MPR mode when the layout change button is used - onLayoutChange: [ - { - commandName: 'toggleHangingProtocol', - commandOptions: { protocolId: 'mpr' }, - context: 'DEFAULT', - }, - ], - // Turns off crosshairs when switching out of MPR mode - }, + callbacks: {}, displaySetSelectors: { activeDisplaySet: { seriesMatchingRules: [ diff --git a/extensions/cornerstone/src/initCornerstoneTools.js b/extensions/cornerstone/src/initCornerstoneTools.js index b40946ff9..cdbf662d4 100644 --- a/extensions/cornerstone/src/initCornerstoneTools.js +++ b/extensions/cornerstone/src/initCornerstoneTools.js @@ -44,6 +44,7 @@ import ImageOverlayViewerTool from './tools/ImageOverlayViewerTool'; export default function initCornerstoneTools(configuration = {}) { CrosshairsTool.isAnnotation = false; ReferenceLinesTool.isAnnotation = false; + AdvancedMagnifyTool.isAnnotation = false; init(configuration); addTool(PanTool); diff --git a/extensions/default/src/Panels/PanelStudyBrowser.tsx b/extensions/default/src/Panels/PanelStudyBrowser.tsx index e1b65d401..e1e6c30c0 100644 --- a/extensions/default/src/Panels/PanelStudyBrowser.tsx +++ b/extensions/default/src/Panels/PanelStudyBrowser.tsx @@ -25,7 +25,8 @@ function PanelStudyBrowser({ // doesn't have to have such an intense shape. This works well enough for now. // Tabs --> Studies --> DisplaySets --> Thumbnails const { StudyInstanceUIDs } = useImageViewer(); - const [{ activeViewportId, viewports }, viewportGridService] = useViewportGrid(); + const [{ activeViewportId, viewports, isHangingProtocolLayout }, viewportGridService] = + useViewportGrid(); const [activeTabName, setActiveTabName] = useState('primary'); const [expandedStudyInstanceUIDs, setExpandedStudyInstanceUIDs] = useState([ ...StudyInstanceUIDs, @@ -40,7 +41,8 @@ function PanelStudyBrowser({ try { updatedViewports = hangingProtocolService.getViewportsRequireUpdate( viewportId, - displaySetInstanceUID + displaySetInstanceUID, + isHangingProtocolLayout ); } catch (error) { console.warn(error); diff --git a/extensions/default/src/Toolbar/ToolbarLayoutSelector.tsx b/extensions/default/src/Toolbar/ToolbarLayoutSelector.tsx index e39066d21..0f4b2e0c8 100644 --- a/extensions/default/src/Toolbar/ToolbarLayoutSelector.tsx +++ b/extensions/default/src/Toolbar/ToolbarLayoutSelector.tsx @@ -58,6 +58,10 @@ const generateAdvancedPresets = ({ servicesManager }) => { } const displaySetInsaneUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId); + if (!displaySetInsaneUIDs) { + return []; + } + const displaySets = displaySetInsaneUIDs.map(uid => displaySetService.getDisplaySetByUID(uid)); return hangingProtocols diff --git a/extensions/default/src/commandsModule.ts b/extensions/default/src/commandsModule.ts index e302e860b..76507b203 100644 --- a/extensions/default/src/commandsModule.ts +++ b/extensions/default/src/commandsModule.ts @@ -286,6 +286,7 @@ const commandsModule = ({ const findOrCreateViewport = layoutFindOrCreate.bind( null, hangingProtocolService, + isHangingProtocolLayout, stateReduce.viewportsByPosition ); @@ -303,7 +304,7 @@ const commandsModule = ({ toggleOneUp() { const viewportGridState = viewportGridService.getState(); - const { activeViewportId, viewports, layout } = viewportGridState; + const { activeViewportId, viewports, layout, isHangingProtocolLayout } = viewportGridState; const { displaySetInstanceUIDs, displaySetOptions, viewportOptions } = viewports.get(activeViewportId); @@ -330,7 +331,8 @@ const commandsModule = ({ .map(displaySetInstanceUID => hangingProtocolService.getViewportsRequireUpdate( viewportIdToUpdate, - displaySetInstanceUID + displaySetInstanceUID, + isHangingProtocolLayout ) ) .flat(); @@ -511,7 +513,8 @@ const commandsModule = ({ currentDisplaySets.sort(dsSortFn); - const { activeViewportId, viewports } = viewportGridService.getState(); + const { activeViewportId, viewports, isHangingProtocolLayout } = + viewportGridService.getState(); const { displaySetInstanceUIDs } = viewports.get(activeViewportId); @@ -545,7 +548,8 @@ const commandsModule = ({ try { updatedViewports = hangingProtocolService.getViewportsRequireUpdate( activeViewportId, - displaySetInstanceUID + displaySetInstanceUID, + isHangingProtocolLayout ); } catch (error) { console.warn(error); diff --git a/extensions/default/src/findViewportsByPosition.ts b/extensions/default/src/findViewportsByPosition.ts index c5fbafd35..7d0d10f83 100644 --- a/extensions/default/src/findViewportsByPosition.ts +++ b/extensions/default/src/findViewportsByPosition.ts @@ -16,6 +16,7 @@ import { StateSyncService } from '@ohif/core'; */ export const findOrCreateViewport = ( hangingProtocolService, + isHangingProtocolLayout, viewportsByPosition, position: number, positionId: string, @@ -31,8 +32,13 @@ export const findOrCreateViewport = ( if (!options.inDisplay) { options.inDisplay = [...viewportsByPosition.initialInDisplay]; } - // See if there is a default viewport for new views. - const missing = hangingProtocolService.getMissingViewport(protocolId, stageIndex, options); + + // See if there is a default viewport for new views + const missing = hangingProtocolService.getMissingViewport( + isHangingProtocolLayout ? protocolId : 'default', + stageIndex, + options + ); if (missing) { const displaySetInstanceUIDs = missing.displaySetsInfo.map(it => it.displaySetInstanceUID); options.inDisplay.push(...displaySetInstanceUIDs); diff --git a/extensions/default/src/getSopClassHandlerModule.js b/extensions/default/src/getSopClassHandlerModule.js index bdbb7057d..28a677ff7 100644 --- a/extensions/default/src/getSopClassHandlerModule.js +++ b/extensions/default/src/getSopClassHandlerModule.js @@ -37,6 +37,8 @@ function getDisplaySetInfo(instances) { const { isDynamicVolume, timePoints } = dynamicVolumeInfo; let displaySetInfo; + const { appConfig } = appContext; + if (isDynamicVolume) { const timePoint = timePoints[0]; const instancesMap = new Map(); @@ -46,9 +48,9 @@ function getDisplaySetInfo(instances) { const firstTimePointInstances = timePoint.map(imageId => instancesMap.get(imageId)); - displaySetInfo = isDisplaySetReconstructable(firstTimePointInstances); + displaySetInfo = isDisplaySetReconstructable(firstTimePointInstances, appConfig); } else { - displaySetInfo = isDisplaySetReconstructable(instances); + displaySetInfo = isDisplaySetReconstructable(instances, appConfig); } return { diff --git a/extensions/measurement-tracking/package.json b/extensions/measurement-tracking/package.json index ff87ce3aa..24f1d4ba8 100644 --- a/extensions/measurement-tracking/package.json +++ b/extensions/measurement-tracking/package.json @@ -32,8 +32,8 @@ "start": "yarn run dev" }, "peerDependencies": { - "@cornerstonejs/core": "^1.70.8", - "@cornerstonejs/tools": "^1.70.8", + "@cornerstonejs/core": "^1.70.9", + "@cornerstonejs/tools": "^1.70.9", "@ohif/core": "3.8.0-beta.85", "@ohif/extension-cornerstone-dicom-sr": "3.8.0-beta.85", "@ohif/ui": "3.8.0-beta.85", diff --git a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx index 4d9dc3a4d..05cce40ec 100644 --- a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx +++ b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx @@ -34,7 +34,8 @@ function PanelStudyBrowserTracking({ // doesn't have to have such an intense shape. This works well enough for now. // Tabs --> Studies --> DisplaySets --> Thumbnails const { StudyInstanceUIDs } = useImageViewer(); - const [{ activeViewportId, viewports }, viewportGridService] = useViewportGrid(); + const [{ activeViewportId, viewports, isHangingProtocolLayout }, viewportGridService] = + useViewportGrid(); const [trackedMeasurements, sendTrackedMeasurementsEvent] = useTrackedMeasurements(); const [activeTabName, setActiveTabName] = useState('primary'); const [expandedStudyInstanceUIDs, setExpandedStudyInstanceUIDs] = useState([ @@ -51,7 +52,8 @@ function PanelStudyBrowserTracking({ try { updatedViewports = hangingProtocolService.getViewportsRequireUpdate( viewportId, - displaySetInstanceUID + displaySetInstanceUID, + isHangingProtocolLayout ); } catch (error) { console.warn(error); diff --git a/modes/basic-test-mode/src/moreTools.ts b/modes/basic-test-mode/src/moreTools.ts index d1f34ec09..98ef45de6 100644 --- a/modes/basic-test-mode/src/moreTools.ts +++ b/modes/basic-test-mode/src/moreTools.ts @@ -138,8 +138,8 @@ const moreTools = [ createButton({ id: 'Magnify', icon: 'tool-magnify', - label: 'Magnify', - tooltip: 'Magnify', + label: 'Zoom-in', + tooltip: 'Zoom-in', commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }), @@ -169,8 +169,8 @@ const moreTools = [ createButton({ id: 'AdvancedMagnify', icon: 'icon-tool-loupe', - label: 'Loupe', - tooltip: 'Loupe', + label: 'Magnify Probe', + tooltip: 'Magnify Probe', commands: 'toggleActiveDisabledToolbar', evaluate: 'evaluate.cornerstoneTool.toggle.ifStrictlyDisabled', }), diff --git a/modes/longitudinal/src/moreTools.ts b/modes/longitudinal/src/moreTools.ts index 101aebf80..0550ef839 100644 --- a/modes/longitudinal/src/moreTools.ts +++ b/modes/longitudinal/src/moreTools.ts @@ -138,8 +138,8 @@ const moreTools = [ createButton({ id: 'Magnify', icon: 'tool-magnify', - label: 'Magnify', - tooltip: 'Magnify', + label: 'Zoom-in', + tooltip: 'Zoom-in', commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }), @@ -161,8 +161,8 @@ const moreTools = [ createButton({ id: 'AdvancedMagnify', icon: 'icon-tool-loupe', - label: 'Loupe', - tooltip: 'Loupe', + label: 'Magnify Probe', + tooltip: 'Magnify Probe', commands: 'toggleActiveDisabledToolbar', evaluate: 'evaluate.cornerstoneTool.toggle.ifStrictlyDisabled', }), diff --git a/modes/segmentation/src/toolbarButtons.ts b/modes/segmentation/src/toolbarButtons.ts index 1d1b445be..11229f031 100644 --- a/modes/segmentation/src/toolbarButtons.ts +++ b/modes/segmentation/src/toolbarButtons.ts @@ -207,8 +207,8 @@ const toolbarButtons: Button[] = [ createButton({ id: 'Magnify', icon: 'tool-magnify', - label: 'Magnify', - tooltip: 'Magnify', + label: 'Zoom-in', + tooltip: 'Zoom-in', commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }), @@ -238,8 +238,8 @@ const toolbarButtons: Button[] = [ createButton({ id: 'AdvancedMagnify', icon: 'icon-tool-loupe', - label: 'Loupe', - tooltip: 'Loupe', + label: 'Magnify Probe', + tooltip: 'Magnify Probe', commands: 'toggleActiveDisabledToolbar', evaluate: 'evaluate.cornerstoneTool.toggle.ifStrictlyDisabled', }), diff --git a/platform/app/cypress/integration/study-list/OHIFStudyList.spec.js b/platform/app/cypress/integration/study-list/OHIFStudyList.spec.js index e59df25cf..437880aaa 100644 --- a/platform/app/cypress/integration/study-list/OHIFStudyList.spec.js +++ b/platform/app/cypress/integration/study-list/OHIFStudyList.spec.js @@ -47,6 +47,8 @@ describe('OHIF Study List', function () { '[data-cy="mode-basic-test-1.3.6.1.4.1.25403.345050719074.3824.20170125113417.1"]' ).click(); cy.get('[data-cy="return-to-work-list"]').click(); + cy.wait(2000); + cy.get('@searchResult2').should($list => { expect($list.length).to.be.eq(1); expect($list).to.contain('Juno'); @@ -72,6 +74,8 @@ describe('OHIF Study List', function () { '[data-cy="mode-basic-test-1.3.6.1.4.1.25403.345050719074.3824.20170125113417.1"]' ).click(); cy.get('[data-cy="return-to-work-list"]').click(); + cy.wait(2000); + cy.get('@searchResult2').should($list => { expect($list.length).to.be.eq(1); expect($list).to.contain('0000003'); @@ -82,6 +86,7 @@ describe('OHIF Study List', function () { cy.get('@AccessionNumber').type('321'); //Wait result list to be displayed cy.waitStudyList(); + cy.wait(2000); cy.get('@searchResult2').should($list => { expect($list.length).to.be.eq(1); expect($list).to.contain('321'); @@ -92,11 +97,15 @@ describe('OHIF Study List', function () { cy.get('@AccessionNumber').type('0000155811'); //Wait result list to be displayed cy.waitStudyList(); + cy.wait(2000); + cy.get('[data-cy="studyRow-1.3.6.1.4.1.25403.345050719074.3824.20170125113417.1"]').click(); cy.get( '[data-cy="mode-basic-test-1.3.6.1.4.1.25403.345050719074.3824.20170125113417.1"]' ).click(); cy.get('[data-cy="return-to-work-list"]').click(); + cy.wait(2000); + cy.get('@searchResult2').should($list => { expect($list.length).to.be.eq(1); expect($list).to.contain('0000155811'); @@ -107,6 +116,8 @@ describe('OHIF Study List', function () { cy.get('@StudyDescription').type('PETCT'); //Wait result list to be displayed cy.waitStudyList(); + cy.wait(2000); + cy.get('@searchResult2').should($list => { expect($list.length).to.be.eq(1); expect($list).to.contain('PETCT'); @@ -117,13 +128,15 @@ describe('OHIF Study List', function () { cy.get('@StudyDescription').type('PETCT'); //Wait result list to be displayed cy.waitStudyList(); + cy.wait(2000); + cy.get('[data-cy="studyRow-1.3.6.1.4.1.25403.345050719074.3824.20170125113417.1"]').click(); cy.get( '[data-cy="mode-basic-test-1.3.6.1.4.1.25403.345050719074.3824.20170125113417.1"]' ).click(); cy.get('[data-cy="return-to-work-list"]').click(); + cy.wait(2000); - cy.wait(1000); cy.get('@searchResult2').should($list => { expect($list.length).to.be.eq(1); expect($list).to.contain('PETCT'); diff --git a/platform/app/package.json b/platform/app/package.json index 9215119e6..fd75a8e8f 100644 --- a/platform/app/package.json +++ b/platform/app/package.json @@ -54,7 +54,7 @@ "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2", "@cornerstonejs/codec-openjph": "^2.4.5", - "@cornerstonejs/dicom-image-loader": "^1.70.8", + "@cornerstonejs/dicom-image-loader": "^1.70.9", "@emotion/serialize": "^1.1.3", "@ohif/core": "3.8.0-beta.85", "@ohif/extension-cornerstone": "3.8.0-beta.85", diff --git a/platform/app/src/App.tsx b/platform/app/src/App.tsx index ec170ab40..35a9c094b 100644 --- a/platform/app/src/App.tsx +++ b/platform/app/src/App.tsx @@ -62,6 +62,13 @@ function App({ config, defaultExtensions, defaultModes }) { const appConfigState = init.appConfig; const { routerBasename, modes, dataSources, oidc, showStudyList } = appConfigState; + // get the maximum 3D texture size + const canvas = document.createElement('canvas'); + const gl = canvas.getContext('webgl2'); + + const max3DTextureSize = gl.getParameter(gl.MAX_3D_TEXTURE_SIZE); + appConfigState.max3DTextureSize = max3DTextureSize; + const { uiDialogService, uiModalService, diff --git a/platform/app/src/components/ViewportGrid.tsx b/platform/app/src/components/ViewportGrid.tsx index ab0e09112..45a74910a 100644 --- a/platform/app/src/components/ViewportGrid.tsx +++ b/platform/app/src/components/ViewportGrid.tsx @@ -103,20 +103,12 @@ function ViewerViewportGrid(props) { const _getUpdatedViewports = useCallback( (viewportId, displaySetInstanceUID) => { - if (!isHangingProtocolLayout) { - return [ - { - viewportId, - displaySetInstanceUIDs: [displaySetInstanceUID], - }, - ]; - } - let updatedViewports = []; try { updatedViewports = hangingProtocolService.getViewportsRequireUpdate( viewportId, - displaySetInstanceUID + displaySetInstanceUID, + isHangingProtocolLayout ); } catch (error) { console.warn(error); diff --git a/platform/core/package.json b/platform/core/package.json index c70ae3a3a..c3f16be3a 100644 --- a/platform/core/package.json +++ b/platform/core/package.json @@ -37,7 +37,7 @@ "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2", "@cornerstonejs/codec-openjph": "^2.4.2", - "@cornerstonejs/dicom-image-loader": "^1.70.8", + "@cornerstonejs/dicom-image-loader": "^1.70.9", "@ohif/ui": "3.8.0-beta.85", "cornerstone-math": "0.1.9", "dicom-parser": "^1.8.21" diff --git a/platform/core/src/services/HangingProtocolService/HangingProtocolService.ts b/platform/core/src/services/HangingProtocolService/HangingProtocolService.ts index 524a45615..317d7ae41 100644 --- a/platform/core/src/services/HangingProtocolService/HangingProtocolService.ts +++ b/platform/core/src/services/HangingProtocolService/HangingProtocolService.ts @@ -601,17 +601,25 @@ export default class HangingProtocolService extends PubSubService { }; } - getViewportsRequireUpdate(viewportId, displaySetInstanceUID) { - const { displaySetService } = this._servicesManager.services; - const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID); - if (displaySet?.unsupported) { - throw new Error('Unsupported displaySet'); - } + /** + * This will return the viewports that need to be updated based on the + * hanging protocol layout and the displaySetInstanceUID that needs to be updated. + * + * This is useful, when for instance we drag and drop a displaySet into a viewport + * which is in MPR, and we need to update the other viewports that are showing the same + * layout. + * + * However, sometimes since we get out of sync with the hanging protocol layout, when + * the user use the custom grid layout, we should not update the other viewports, and that is + * when the isHangingProtocolLayout is set to false. + * + * @param viewportId - the id of the viewport that needs to be updated + * @param displaySetInstanceUID - the displaySetInstanceUID that needs to be updated + * @param isHangingProtocolLayout - whether the layout is a hanging protocol layout + * @returns + */ + getViewportsRequireUpdate(viewportId, displaySetInstanceUID, isHangingProtocolLayout = true) { const newDisplaySetInstanceUID = displaySetInstanceUID; - const protocol = this.protocol; - const protocolStage = protocol.stages[this.stageIndex]; - const protocolViewports = protocolStage.viewports; - const defaultReturn = [ { viewportId, @@ -619,6 +627,19 @@ export default class HangingProtocolService extends PubSubService { }, ]; + if (!isHangingProtocolLayout) { + return defaultReturn; + } + + const { displaySetService } = this._servicesManager.services; + const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID); + if (displaySet?.unsupported) { + throw new Error('Unsupported displaySet'); + } + const protocol = this.protocol; + const protocolStage = protocol.stages[this.stageIndex]; + const protocolViewports = protocolStage.viewports; + if (!protocolViewports) { return defaultReturn; } @@ -1084,9 +1105,8 @@ export default class HangingProtocolService extends PubSubService { options ): HangingProtocol.ViewportMatchDetails { if (this.protocol.id !== protocolId) { - throw new Error( - `Currently applied protocol ${this.protocol.id} is different from ${protocolId}` - ); + console.warn('setting protocol'); + this.protocol = this.getProtocolById(protocolId); } const protocol = this.protocol; const stage = protocol.stages[stageIdx]; diff --git a/platform/core/src/utils/isDisplaySetReconstructable.js b/platform/core/src/utils/isDisplaySetReconstructable.js index c32b565b0..00decb83a 100644 --- a/platform/core/src/utils/isDisplaySetReconstructable.js +++ b/platform/core/src/utils/isDisplaySetReconstructable.js @@ -10,7 +10,7 @@ const iopTolerance = 0.01; * * @param {Object[]} instances An array of `OHIFInstanceMetadata` objects. */ -export default function isDisplaySetReconstructable(instances) { +export default function isDisplaySetReconstructable(instances, appConfig) { if (!instances.length) { return { value: false }; } @@ -18,6 +18,14 @@ export default function isDisplaySetReconstructable(instances) { const isMultiframe = firstInstance.NumberOfFrames > 1; + if (appConfig) { + const rows = toNumber(firstInstance.Rows); + const columns = toNumber(firstInstance.Columns); + + if (rows > appConfig.max3DTextureSize || columns > appConfig.max3DTextureSize) { + return { value: false }; + } + } // We used to check is reconstructable modalities here, but the logic is removed // in favor of the calculation by metadata (orientation and positions) diff --git a/platform/docs/docs/migration-guide/from-3p7-to-3p8.md b/platform/docs/docs/migration-guide/from-3p7-to-3p8.md index 48ea73890..dde25c341 100644 --- a/platform/docs/docs/migration-guide/from-3p7-to-3p8.md +++ b/platform/docs/docs/migration-guide/from-3p7-to-3p8.md @@ -135,3 +135,8 @@ Additional Resources ## leftPanelDefaultClosed and rightPanelDefaultClosed Now they are renamed to `leftPanelClosed` and `rightPanelClosed` respectively. + + +## StudyInstanceUID in the URL param + +Previously there were two params that you could choose: seriesInstanceUID and seriesInstanceUIDs, they have been replaced with seriesInstanceUIDs so even if you would like to filter one series use ``seriesInstanceUIDs` diff --git a/platform/docs/docs/platform/services/data/StateSyncService.md b/platform/docs/docs/platform/services/data/StateSyncService.md index 6688c5eca..49fdbf0c3 100644 --- a/platform/docs/docs/platform/services/data/StateSyncService.md +++ b/platform/docs/docs/platform/services/data/StateSyncService.md @@ -69,7 +69,6 @@ list as state stores are added: * `viewportGridStore` has viewport grid restore information for returning to an earlier grid layout. * `reuseIdMap` has a map of names to display sets for preserving user changes to hp display set selections. * `hanging` has a map of the hanging protocol stage information applied (HPInfo) -* `toggleHangingProtocol` has the previously applied hanging protocol, to toggle an HP off. ### Cornerstone Extension Stores diff --git a/platform/ui/src/components/Thumbnail/Thumbnail.tsx b/platform/ui/src/components/Thumbnail/Thumbnail.tsx index 578f4a73f..c527102d1 100644 --- a/platform/ui/src/components/Thumbnail/Thumbnail.tsx +++ b/platform/ui/src/components/Thumbnail/Thumbnail.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import { useDrag } from 'react-dnd'; @@ -35,6 +35,19 @@ const Thumbnail = ({ }, }); + const [lastTap, setLastTap] = useState(0); + + const handleTouchEnd = e => { + const currentTime = new Date().getTime(); + const tapLength = currentTime - lastTap; + if (tapLength < 300 && tapLength > 0) { + onDoubleClick(e); + } else { + onClick(e); + } + setLastTap(currentTime); + }; + return (
@@ -56,9 +70,6 @@ const Thumbnail = ({ ? 'border-primary-light border-2' : 'border-secondary-light border hover:border-blue-300' )} - style={{ - margin: isActive ? '0' : '1px', - }} > {imageSrc ? ( { + const currentTime = new Date().getTime(); + const tapLength = currentTime - lastTap; + if (tapLength < 300 && tapLength > 0) { + onDoubleClick(e); + } else { + onClick(e); + } + setLastTap(currentTime); + }; + return (
(); - // Options is a temporary state store which can be used by the // findOrCreate to store state about already found viewports. Typically, // it will be used to store the display set UID's which are already @@ -237,7 +236,21 @@ export function ViewportGridProvider({ children, service }) { for (let col = 0; col < numCols; col++) { const position = col + row * numCols; const layoutOption = layoutOptions[position]; - const positionId = layoutOption?.positionId || `${col}-${row}`; + + let xPos, yPos, w, h; + if (layoutOptions && layoutOptions[position]) { + ({ x: xPos, y: yPos, width: w, height: h } = layoutOptions[position]); + } else { + w = 1 / numCols; + h = 1 / numRows; + xPos = col * w; + yPos = row * h; + } + + const colIndex = Math.round(xPos * numCols); + const rowIndex = Math.round(yPos * numRows); + + const positionId = layoutOption?.positionId || `${colIndex}-${rowIndex}`; if (hasOptions && position >= layoutOptions.length) { continue; @@ -264,16 +277,6 @@ export function ViewportGridProvider({ children, service }) { // and it is part of the read only state viewports.set(viewport.viewportId, viewport); - let xPos, yPos, w, h; - if (layoutOptions && layoutOptions[position]) { - ({ x: xPos, y: yPos, width: w, height: h } = layoutOptions[position]); - } else { - w = 1 / numCols; - h = 1 / numRows; - xPos = col * w; - yPos = row * h; - } - Object.assign(viewport, { width: w, height: h, diff --git a/yarn.lock b/yarn.lock index 66118a7a4..a0b48fbf2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1497,13 +1497,13 @@ resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== -"@cornerstonejs/adapters@^1.70.8": - version "1.70.8" - resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-1.70.8.tgz#b812a2ce00cf48404703237399e2c347d2e29d90" - integrity sha512-dShI7qQdG/1l3JqeBhaBFT8RmfEHUGLfvKby3tPh6bgo5bj+2KRcnlW1hmDuAHxWIkCDnftoExSUIJqXGlvbdA== +"@cornerstonejs/adapters@^1.70.9": + version "1.70.9" + resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-1.70.9.tgz#77f96dbdd2d53225ad8d13d4912afa3538c84baa" + integrity sha512-eGJu9Skb2WUYvXn1JNO/5NB3EMEHj9rGAelrTozFclqPjnfHbxXlMBeScdE8YTmwR6wHtwiB/lXgu6hhrbYOMQ== dependencies: "@babel/runtime-corejs2" "^7.17.8" - "@cornerstonejs/tools" "^1.70.8" + "@cornerstonejs/tools" "^1.70.9" buffer "^6.0.3" dcmjs "^0.29.8" gl-matrix "^3.4.3" @@ -1550,10 +1550,10 @@ resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjph/-/codec-openjph-2.4.5.tgz#8690b61a86fa53ef38a70eee9d665a79229517c0" integrity sha512-MZCUy8VG0VG5Nl1l58+g+kH3LujAzLYTfJqkwpWI2gjSrGXnP6lgwyy4GmPRZWVoS40/B1LDNALK905cNWm+sg== -"@cornerstonejs/core@^1.70.8": - version "1.70.8" - resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-1.70.8.tgz#e66731986de638ebd7307fec19508544bb5fe7dc" - integrity sha512-xDAHxw4eGqJ4sPHc/+wPdRhZd2CYCkEuA7dE43e4rS6XKAN2X2BCjvTlZkAjECnOIsAnIOg//Pq7V6I09L28vg== +"@cornerstonejs/core@^1.70.9": + version "1.70.9" + resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-1.70.9.tgz#c08aa055a786aac79c457e744f18c5fe3f3203f5" + integrity sha512-2Gu6SXRKuWFoP0aD0dC5et5pcWExxJOE2DWXUKuVdXollbIC+uq3I5FSWOwM8Zzu/Z3JzWWY5IYYPKdJqb8ogQ== dependencies: "@kitware/vtk.js" "30.3.3" comlink "^4.4.1" @@ -1561,34 +1561,34 @@ gl-matrix "^3.4.3" lodash.clonedeep "4.5.0" -"@cornerstonejs/dicom-image-loader@^1.70.8": - version "1.70.8" - resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-1.70.8.tgz#e8107177298c0e9b314c225b93b0e28db59c8142" - integrity sha512-YoTN/gINulfZr80tecbwqijNwm+UpbRuq6gOACmXGQ4y9MjT78p8e6NMdvU/40rwT41FR3KNySbKEXJSDRURgA== +"@cornerstonejs/dicom-image-loader@^1.70.9": + version "1.70.9" + resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-1.70.9.tgz#e329eb86343651f3d6148e03eb1e9d4390b79a6a" + integrity sha512-M4qd88NA+z62d8w6eaDUbYfFCn3dmDqGkF0iI2s67LlySmeDo7pUmSG8gQbuJMOUPQMxD1Hy8EXBexaTAsBiaQ== dependencies: "@cornerstonejs/codec-charls" "^1.2.3" "@cornerstonejs/codec-libjpeg-turbo-8bit" "^1.2.2" "@cornerstonejs/codec-openjpeg" "^1.2.2" "@cornerstonejs/codec-openjph" "^2.4.5" - "@cornerstonejs/core" "^1.70.8" + "@cornerstonejs/core" "^1.70.9" dicom-parser "^1.8.9" pako "^2.0.4" uuid "^9.0.0" -"@cornerstonejs/streaming-image-volume-loader@^1.70.8": - version "1.70.8" - resolved "https://registry.yarnpkg.com/@cornerstonejs/streaming-image-volume-loader/-/streaming-image-volume-loader-1.70.8.tgz#97a6b72b254564aef9b3b4e10896d5ed42b13c50" - integrity sha512-d53GB3Q0TMWOOzW9kmD03OUFCfTrVRoM/0O2ZRNdrtkj5H87YYLtKSyJsFrey7kC+3VfNVd+KLu4H0RsW/SulQ== +"@cornerstonejs/streaming-image-volume-loader@^1.70.9": + version "1.70.9" + resolved "https://registry.yarnpkg.com/@cornerstonejs/streaming-image-volume-loader/-/streaming-image-volume-loader-1.70.9.tgz#3f3b7c04b3db6c303cb278e5ab8d1916cd91a3b1" + integrity sha512-Uay1IIRJMge0Tp7DrgisaqIXowh5IdROXbexmbGVT++0qeyzIQIzXdaUrJpuXDpaB2bYppWEY9moKja0mtlBHw== dependencies: - "@cornerstonejs/core" "^1.70.8" + "@cornerstonejs/core" "^1.70.9" comlink "^4.4.1" -"@cornerstonejs/tools@^1.70.8": - version "1.70.8" - resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-1.70.8.tgz#2318f8d9a31ff77692fe2c7ec3ed1a758b82d13a" - integrity sha512-/hS8FckYtPXK5OYnDPzJUIjMgTyaYQOkwxo0eoRYwbCPFrHoqeT87drSdWdXm2RWR7fR+95ScRNaB1ZGBPEqcA== +"@cornerstonejs/tools@^1.70.9": + version "1.70.9" + resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-1.70.9.tgz#e71b709ba5652918a9c92ad344638d3188d8af2b" + integrity sha512-/Pbbb/hAeYd0bgrXtAEwQOksdL5qy+zwosk3HMsUCa5S7FnvEOSnSQbs0h8UZwKIkW09LtzAkSiPgJpP7A3cwg== dependencies: - "@cornerstonejs/core" "^1.70.8" + "@cornerstonejs/core" "^1.70.9" "@icr/polyseg-wasm" "0.4.0" "@types/offscreencanvas" "2019.7.3" comlink "^4.4.1" @@ -2086,7 +2086,7 @@ "@docusaurus/theme-search-algolia" "2.4.3" "@docusaurus/types" "2.4.3" -"@docusaurus/react-loadable@5.5.2": +"@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2": version "5.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce" integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ== @@ -17743,14 +17743,6 @@ react-loadable-ssr-addon-v5-slorber@^1.0.1: dependencies: "@babel/runtime" "^7.10.3" -"react-loadable@npm:@docusaurus/react-loadable@5.5.2": - version "5.5.2" - resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce" - integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ== - dependencies: - "@types/react" "*" - prop-types "^15.6.2" - react-modal@3.11.2: version "3.11.2" resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.11.2.tgz#bad911976d4add31aa30dba8a41d11e21c4ac8a4" @@ -19541,7 +19533,7 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -19559,15 +19551,6 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" @@ -19663,7 +19646,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -19691,13 +19674,6 @@ strip-ansi@^5.1.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.0, strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -21741,7 +21717,7 @@ worker-loader@3.0.8, worker-loader@^3.0.8: loader-utils "^2.0.0" schema-utils "^3.0.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -21767,15 +21743,6 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"