diff --git a/extensions/default/src/commandsModule.ts b/extensions/default/src/commandsModule.ts index b2a80cbfe..a14563e93 100644 --- a/extensions/default/src/commandsModule.ts +++ b/extensions/default/src/commandsModule.ts @@ -329,7 +329,7 @@ const commandsModule = ({ uiNotificationService.show({ title: 'Change Stage', message: 'The hanging protocol has no more applicable stages', - type: 'error', + type: 'info', duration: 3000, }); }, diff --git a/extensions/tmtv/src/getHangingProtocolModule.js b/extensions/tmtv/src/getHangingProtocolModule.js index b58e55d42..63a9ba055 100644 --- a/extensions/tmtv/src/getHangingProtocolModule.js +++ b/extensions/tmtv/src/getHangingProtocolModule.js @@ -1,3 +1,219 @@ +import { + ctAXIAL, + ctCORONAL, + ctSAGITTAL, + fusionAXIAL, + fusionCORONAL, + fusionSAGITTAL, + mipSAGITTAL, + ptAXIAL, + ptCORONAL, + ptSAGITTAL, +} from './utils/hpViewports'; + +/** + * represents a 3x4 viewport layout configuration. The layout displays CT axial, sagittal, and coronal + * images in the first row, PT axial, sagittal, and coronal images in the second row, and fusion axial, + * sagittal, and coronal images in the third row. The fourth column is fully spanned by a MIP sagittal + * image, covering all three rows. It has synchronizers for windowLevel for all CT and PT images, and + * also camera synchronizer for each orientation + */ +const stage1 = { + name: 'default', + viewportStructure: { + layoutType: 'grid', + properties: { + rows: 3, + columns: 4, + layoutOptions: [ + { + x: 0, + y: 0, + width: 1 / 4, + height: 1 / 3, + }, + { + x: 1 / 4, + y: 0, + width: 1 / 4, + height: 1 / 3, + }, + { + x: 2 / 4, + y: 0, + width: 1 / 4, + height: 1 / 3, + }, + { + x: 0, + y: 1 / 3, + width: 1 / 4, + height: 1 / 3, + }, + { + x: 1 / 4, + y: 1 / 3, + width: 1 / 4, + height: 1 / 3, + }, + { + x: 2 / 4, + y: 1 / 3, + width: 1 / 4, + height: 1 / 3, + }, + { + x: 0, + y: 2 / 3, + width: 1 / 4, + height: 1 / 3, + }, + { + x: 1 / 4, + y: 2 / 3, + width: 1 / 4, + height: 1 / 3, + }, + { + x: 2 / 4, + y: 2 / 3, + width: 1 / 4, + height: 1 / 3, + }, + { + x: 3 / 4, + y: 0, + width: 1 / 4, + height: 1, + }, + ], + }, + }, + viewports: [ + ctAXIAL, + ctSAGITTAL, + ctCORONAL, + ptAXIAL, + ptSAGITTAL, + ptCORONAL, + fusionAXIAL, + fusionSAGITTAL, + fusionCORONAL, + mipSAGITTAL, + ], + createdDate: '2021-02-23T18:32:42.850Z', +}; + +/** + * The layout displays CT axial image in the top-left viewport, fusion axial image + * in the top-right viewport, PT axial image in the bottom-left viewport, and MIP + * sagittal image in the bottom-right viewport. The layout follows a simple grid + * pattern with 2 rows and 2 columns. It includes synchronizers as well. + */ +const stage2 = { + name: 'Fusion 2x2', + viewportStructure: { + layoutType: 'grid', + properties: { + rows: 2, + columns: 2, + }, + }, + viewports: [ctAXIAL, fusionAXIAL, ptAXIAL, mipSAGITTAL], +}; + +/** + * The top row displays CT images in axial, sagittal, and coronal orientations from + * left to right, respectively. The bottom row displays PT images in axial, sagittal, + * and coronal orientations from left to right, respectively. + * The layout follows a simple grid pattern with 2 rows and 3 columns. + * It includes synchronizers as well. + */ +const stage3 = { + name: '2x3-layout', + viewportStructure: { + layoutType: 'grid', + properties: { + rows: 2, + columns: 3, + }, + }, + viewports: [ctAXIAL, ctSAGITTAL, ctCORONAL, ptAXIAL, ptSAGITTAL, ptCORONAL], +}; + +/** + * In this layout, the top row displays PT images in coronal, sagittal, and axial + * orientations from left to right, respectively, followed by a MIP sagittal image + * that spans both rows on the rightmost side. The bottom row displays fusion images + * in coronal, sagittal, and axial orientations from left to right, respectively. + * There is no viewport in the bottom row's rightmost position, as the MIP sagittal viewport + * from the top row spans the full height of both rows. + * It includes synchronizers as well. + */ +const stage4 = { + name: '2x4-layout', + viewportStructure: { + layoutType: 'grid', + properties: { + rows: 2, + columns: 4, + layoutOptions: [ + { + x: 0, + y: 0, + width: 1 / 4, + height: 1 / 2, + }, + { + x: 1 / 4, + y: 0, + width: 1 / 4, + height: 1 / 2, + }, + { + x: 2 / 4, + y: 0, + width: 1 / 4, + height: 1 / 2, + }, + { + x: 3 / 4, + y: 0, + width: 1 / 4, + height: 1, + }, + { + x: 0, + y: 1 / 2, + width: 1 / 4, + height: 1 / 2, + }, + { + x: 1 / 4, + y: 1 / 2, + width: 1 / 4, + height: 1 / 2, + }, + { + x: 2 / 4, + y: 1 / 2, + width: 1 / 4, + height: 1 / 2, + }, + ], + }, + }, + viewports: [ + ptCORONAL, + ptSAGITTAL, + ptAXIAL, + mipSAGITTAL, + fusionCORONAL, + fusionSAGITTAL, + fusionAXIAL, + ], +}; + const ptCT = { id: '@ohif/extension-tmtv.hangingProtocolModule.ptCT', locked: true, @@ -100,500 +316,7 @@ const ptCT = { }, }, - stages: [ - { - name: 'default', - viewportStructure: { - layoutType: 'grid', - properties: { - rows: 3, - columns: 4, - layoutOptions: [ - { - x: 0, - y: 0, - width: 1 / 4, - height: 1 / 3, - }, - { - x: 1 / 4, - y: 0, - width: 1 / 4, - height: 1 / 3, - }, - { - x: 2 / 4, - y: 0, - width: 1 / 4, - height: 1 / 3, - }, - { - x: 0, - y: 1 / 3, - width: 1 / 4, - height: 1 / 3, - }, - { - x: 1 / 4, - y: 1 / 3, - width: 1 / 4, - height: 1 / 3, - }, - { - x: 2 / 4, - y: 1 / 3, - width: 1 / 4, - height: 1 / 3, - }, - { - x: 0, - y: 2 / 3, - width: 1 / 4, - height: 1 / 3, - }, - { - x: 1 / 4, - y: 2 / 3, - width: 1 / 4, - height: 1 / 3, - }, - { - x: 2 / 4, - y: 2 / 3, - width: 1 / 4, - height: 1 / 3, - }, - { - x: 3 / 4, - y: 0, - width: 1 / 4, - height: 1, - }, - ], - }, - }, - viewports: [ - { - viewportOptions: { - viewportId: 'ctAXIAL', - viewportType: 'volume', - orientation: 'axial', - toolGroupId: 'ctToolGroup', - initialImageOptions: { - // index: 5, - preset: 'first', // 'first', 'last', 'middle' - }, - syncGroups: [ - { - type: 'cameraPosition', - id: 'axialSync', - source: true, - target: true, - }, - { - type: 'voi', - id: 'ctWLSync', - source: true, - target: true, - }, - ], - }, - displaySets: [ - { - id: 'ctDisplaySet', - }, - ], - }, - { - viewportOptions: { - viewportId: 'ctSAGITTAL', - viewportType: 'volume', - orientation: 'sagittal', - toolGroupId: 'ctToolGroup', - syncGroups: [ - { - type: 'cameraPosition', - id: 'sagittalSync', - source: true, - target: true, - }, - { - type: 'voi', - id: 'ctWLSync', - source: true, - target: true, - }, - ], - }, - displaySets: [ - { - id: 'ctDisplaySet', - }, - ], - }, - { - viewportOptions: { - viewportId: 'ctCORONAL', - viewportType: 'volume', - orientation: 'coronal', - toolGroupId: 'ctToolGroup', - syncGroups: [ - { - type: 'cameraPosition', - id: 'coronalSync', - source: true, - target: true, - }, - { - type: 'voi', - id: 'ctWLSync', - source: true, - target: true, - }, - ], - }, - displaySets: [ - { - id: 'ctDisplaySet', - }, - ], - }, - { - viewportOptions: { - viewportId: 'ptAXIAL', - viewportType: 'volume', - background: [1, 1, 1], - orientation: 'axial', - toolGroupId: 'ptToolGroup', - initialImageOptions: { - // index: 5, - preset: 'first', // 'first', 'last', 'middle' - }, - syncGroups: [ - { - type: 'cameraPosition', - id: 'axialSync', - source: true, - target: true, - }, - { - type: 'voi', - id: 'ptWLSync', - source: true, - target: true, - }, - { - type: 'voi', - id: 'ptFusionWLSync', - source: true, - target: false, - }, - ], - }, - displaySets: [ - { - options: { - voi: { - windowWidth: 5, - windowCenter: 2.5, - }, - voiInverted: true, - }, - id: 'ptDisplaySet', - }, - ], - }, - { - viewportOptions: { - viewportId: 'ptSAGITTAL', - viewportType: 'volume', - orientation: 'sagittal', - background: [1, 1, 1], - toolGroupId: 'ptToolGroup', - syncGroups: [ - { - type: 'cameraPosition', - id: 'sagittalSync', - source: true, - target: true, - }, - { - type: 'voi', - id: 'ptWLSync', - source: true, - target: true, - }, - { - type: 'voi', - id: 'ptFusionWLSync', - source: true, - target: false, - }, - ], - }, - displaySets: [ - { - options: { - voi: { - windowWidth: 5, - windowCenter: 2.5, - }, - voiInverted: true, - }, - id: 'ptDisplaySet', - }, - ], - }, - { - viewportOptions: { - viewportId: 'ptCORONAL', - viewportType: 'volume', - orientation: 'coronal', - background: [1, 1, 1], - toolGroupId: 'ptToolGroup', - syncGroups: [ - { - type: 'cameraPosition', - id: 'coronalSync', - source: true, - target: true, - }, - { - type: 'voi', - id: 'ptWLSync', - source: true, - target: true, - }, - { - type: 'voi', - id: 'ptFusionWLSync', - source: true, - target: false, - }, - ], - }, - displaySets: [ - { - options: { - voi: { - windowWidth: 5, - windowCenter: 2.5, - }, - voiInverted: true, - }, - id: 'ptDisplaySet', - }, - ], - }, - { - viewportOptions: { - viewportId: 'fusionAXIAL', - viewportType: 'volume', - orientation: 'axial', - toolGroupId: 'fusionToolGroup', - initialImageOptions: { - // index: 5, - preset: 'first', // 'first', 'last', 'middle' - }, - syncGroups: [ - { - type: 'cameraPosition', - id: 'axialSync', - source: true, - target: true, - }, - { - type: 'voi', - id: 'ctWLSync', - source: false, - target: true, - }, - { - type: 'voi', - id: 'fusionWLSync', - source: true, - target: true, - }, - { - type: 'voi', - id: 'ptFusionWLSync', - source: false, - target: true, - }, - ], - }, - displaySets: [ - { - id: 'ctDisplaySet', - }, - { - options: { - colormap: 'hsv', - voi: { - windowWidth: 5, - windowCenter: 2.5, - }, - }, - id: 'ptDisplaySet', - }, - ], - }, - { - viewportOptions: { - viewportId: 'fusionSAGITTAL', - viewportType: 'volume', - orientation: 'sagittal', - toolGroupId: 'fusionToolGroup', - // initialImageOptions: { - // index: 180, - // preset: 'middle', // 'first', 'last', 'middle' - // }, - syncGroups: [ - { - type: 'cameraPosition', - id: 'sagittalSync', - source: true, - target: true, - }, - { - type: 'voi', - id: 'ctWLSync', - source: false, - target: true, - }, - { - type: 'voi', - id: 'fusionWLSync', - source: true, - target: true, - }, - { - type: 'voi', - id: 'ptFusionWLSync', - source: false, - target: true, - }, - ], - }, - displaySets: [ - { - id: 'ctDisplaySet', - }, - { - options: { - colormap: 'hsv', - voi: { - windowWidth: 5, - windowCenter: 2.5, - }, - }, - id: 'ptDisplaySet', - }, - ], - }, - { - viewportOptions: { - viewportId: 'fusionCoronal', - viewportType: 'volume', - orientation: 'coronal', - toolGroupId: 'fusionToolGroup', - // initialImageOptions: { - // index: 180, - // preset: 'middle', // 'first', 'last', 'middle' - // }, - syncGroups: [ - { - type: 'cameraPosition', - id: 'coronalSync', - source: true, - target: true, - }, - { - type: 'voi', - id: 'ctWLSync', - source: false, - target: true, - }, - { - type: 'voi', - id: 'fusionWLSync', - source: true, - target: true, - }, - { - type: 'voi', - id: 'ptFusionWLSync', - source: false, - target: true, - }, - ], - }, - displaySets: [ - { - id: 'ctDisplaySet', - }, - { - options: { - colormap: 'hsv', - voi: { - windowWidth: 5, - windowCenter: 2.5, - }, - }, - id: 'ptDisplaySet', - }, - ], - }, - { - viewportOptions: { - viewportId: 'mipSagittal', - viewportType: 'volume', - orientation: 'sagittal', - background: [1, 1, 1], - toolGroupId: 'mipToolGroup', - syncGroups: [ - { - type: 'voi', - id: 'ptWLSync', - source: true, - target: true, - }, - { - type: 'voi', - id: 'ptFusionWLSync', - source: true, - target: false, - }, - ], - - // Custom props can be used to set custom properties which extensions - // can react on. - customViewportProps: { - // We use viewportDisplay to filter the viewports which are displayed - // in mip and we set the scrollbar according to their rotation index - // in the cornerstone extension. - hideOverlays: true, - }, - }, - displaySets: [ - { - options: { - blendMode: 'MIP', - slabThickness: 'fullVolume', - voi: { - windowWidth: 5, - windowCenter: 2.5, - }, - voiInverted: true, - }, - id: 'ptDisplaySet', - }, - ], - }, - ], - createdDate: '2021-02-23T18:32:42.850Z', - }, - ], + stages: [stage1, stage2, stage3, stage4], numberOfPriorsReferenced: -1, }; diff --git a/extensions/tmtv/src/utils/hpViewports.ts b/extensions/tmtv/src/utils/hpViewports.ts new file mode 100644 index 000000000..0efc354ec --- /dev/null +++ b/extensions/tmtv/src/utils/hpViewports.ts @@ -0,0 +1,438 @@ +const ctAXIAL = { + viewportOptions: { + viewportId: 'ctAXIAL', + viewportType: 'volume', + orientation: 'axial', + toolGroupId: 'ctToolGroup', + initialImageOptions: { + // index: 5, + preset: 'first', // 'first', 'last', 'middle' + }, + syncGroups: [ + { + type: 'cameraPosition', + id: 'axialSync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'ctWLSync', + source: true, + target: true, + }, + ], + }, + displaySets: [ + { + id: 'ctDisplaySet', + }, + ], +}; + +const ctSAGITTAL = { + viewportOptions: { + viewportId: 'ctSAGITTAL', + viewportType: 'volume', + orientation: 'sagittal', + toolGroupId: 'ctToolGroup', + syncGroups: [ + { + type: 'cameraPosition', + id: 'sagittalSync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'ctWLSync', + source: true, + target: true, + }, + ], + }, + displaySets: [ + { + id: 'ctDisplaySet', + }, + ], +}; +const ctCORONAL = { + viewportOptions: { + viewportId: 'ctCORONAL', + viewportType: 'volume', + orientation: 'coronal', + toolGroupId: 'ctToolGroup', + syncGroups: [ + { + type: 'cameraPosition', + id: 'coronalSync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'ctWLSync', + source: true, + target: true, + }, + ], + }, + displaySets: [ + { + id: 'ctDisplaySet', + }, + ], +}; + +const ptAXIAL = { + viewportOptions: { + viewportId: 'ptAXIAL', + viewportType: 'volume', + background: [1, 1, 1], + orientation: 'axial', + toolGroupId: 'ptToolGroup', + initialImageOptions: { + // index: 5, + preset: 'first', // 'first', 'last', 'middle' + }, + syncGroups: [ + { + type: 'cameraPosition', + id: 'axialSync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'ptWLSync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'ptFusionWLSync', + source: true, + target: false, + }, + ], + }, + displaySets: [ + { + options: { + voi: { + windowWidth: 5, + windowCenter: 2.5, + }, + voiInverted: true, + }, + id: 'ptDisplaySet', + }, + ], +}; + +const ptSAGITTAL = { + viewportOptions: { + viewportId: 'ptSAGITTAL', + viewportType: 'volume', + orientation: 'sagittal', + background: [1, 1, 1], + toolGroupId: 'ptToolGroup', + syncGroups: [ + { + type: 'cameraPosition', + id: 'sagittalSync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'ptWLSync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'ptFusionWLSync', + source: true, + target: false, + }, + ], + }, + displaySets: [ + { + options: { + voi: { + windowWidth: 5, + windowCenter: 2.5, + }, + voiInverted: true, + }, + id: 'ptDisplaySet', + }, + ], +}; + +const ptCORONAL = { + viewportOptions: { + viewportId: 'ptCORONAL', + viewportType: 'volume', + orientation: 'coronal', + background: [1, 1, 1], + toolGroupId: 'ptToolGroup', + syncGroups: [ + { + type: 'cameraPosition', + id: 'coronalSync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'ptWLSync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'ptFusionWLSync', + source: true, + target: false, + }, + ], + }, + displaySets: [ + { + options: { + voi: { + windowWidth: 5, + windowCenter: 2.5, + }, + voiInverted: true, + }, + id: 'ptDisplaySet', + }, + ], +}; + +const fusionAXIAL = { + viewportOptions: { + viewportId: 'fusionAXIAL', + viewportType: 'volume', + orientation: 'axial', + toolGroupId: 'fusionToolGroup', + initialImageOptions: { + // index: 5, + preset: 'first', // 'first', 'last', 'middle' + }, + syncGroups: [ + { + type: 'cameraPosition', + id: 'axialSync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'ctWLSync', + source: false, + target: true, + }, + { + type: 'voi', + id: 'fusionWLSync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'ptFusionWLSync', + source: false, + target: true, + }, + ], + }, + displaySets: [ + { + id: 'ctDisplaySet', + }, + { + options: { + colormap: 'hsv', + voi: { + windowWidth: 5, + windowCenter: 2.5, + }, + }, + id: 'ptDisplaySet', + }, + ], +}; + +const fusionSAGITTAL = { + viewportOptions: { + viewportId: 'fusionSAGITTAL', + viewportType: 'volume', + orientation: 'sagittal', + toolGroupId: 'fusionToolGroup', + // initialImageOptions: { + // index: 180, + // preset: 'middle', // 'first', 'last', 'middle' + // }, + syncGroups: [ + { + type: 'cameraPosition', + id: 'sagittalSync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'ctWLSync', + source: false, + target: true, + }, + { + type: 'voi', + id: 'fusionWLSync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'ptFusionWLSync', + source: false, + target: true, + }, + ], + }, + displaySets: [ + { + id: 'ctDisplaySet', + }, + { + options: { + colormap: 'hsv', + voi: { + windowWidth: 5, + windowCenter: 2.5, + }, + }, + id: 'ptDisplaySet', + }, + ], +}; + +const fusionCORONAL = { + viewportOptions: { + viewportId: 'fusionCoronal', + viewportType: 'volume', + orientation: 'coronal', + toolGroupId: 'fusionToolGroup', + // initialImageOptions: { + // index: 180, + // preset: 'middle', // 'first', 'last', 'middle' + // }, + syncGroups: [ + { + type: 'cameraPosition', + id: 'coronalSync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'ctWLSync', + source: false, + target: true, + }, + { + type: 'voi', + id: 'fusionWLSync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'ptFusionWLSync', + source: false, + target: true, + }, + ], + }, + displaySets: [ + { + id: 'ctDisplaySet', + }, + { + options: { + colormap: 'hsv', + voi: { + windowWidth: 5, + windowCenter: 2.5, + }, + }, + id: 'ptDisplaySet', + }, + ], +}; + +const mipSAGITTAL = { + viewportOptions: { + viewportId: 'mipSagittal', + viewportType: 'volume', + orientation: 'sagittal', + background: [1, 1, 1], + toolGroupId: 'mipToolGroup', + syncGroups: [ + { + type: 'voi', + id: 'ptWLSync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'ptFusionWLSync', + source: true, + target: false, + }, + ], + + // Custom props can be used to set custom properties which extensions + // can react on. + customViewportProps: { + // We use viewportDisplay to filter the viewports which are displayed + // in mip and we set the scrollbar according to their rotation index + // in the cornerstone extension. + hideOverlays: true, + }, + }, + displaySets: [ + { + options: { + blendMode: 'MIP', + slabThickness: 'fullVolume', + voi: { + windowWidth: 5, + windowCenter: 2.5, + }, + voiInverted: true, + }, + id: 'ptDisplaySet', + }, + ], +}; + +export { + ctAXIAL, + ctSAGITTAL, + ctCORONAL, + ptAXIAL, + ptSAGITTAL, + ptCORONAL, + fusionAXIAL, + fusionSAGITTAL, + fusionCORONAL, + mipSAGITTAL, +};