From d5d821464acb0f89fc9b189bd245a06c209d77b4 Mon Sep 17 00:00:00 2001 From: Ibrahim <93064150+IbrahimCSAE@users.noreply.github.com> Date: Fri, 21 Jun 2024 11:22:05 -0400 Subject: [PATCH] feat(HP): Frame View HP (#4235) --- .../src/getHangingProtocolModule.ts | 5 + extensions/cornerstone/src/hps/frameView.ts | 1565 +++++++++++++++++ extensions/cornerstone/src/index.tsx | 4 + .../SyncGroupService/SyncGroupService.ts | 9 + .../synchronizers/frameViewSynchronizer.ts | 51 + 5 files changed, 1634 insertions(+) create mode 100644 extensions/cornerstone/src/hps/frameView.ts create mode 100644 extensions/cornerstone/src/synchronizers/frameViewSynchronizer.ts diff --git a/extensions/cornerstone/src/getHangingProtocolModule.ts b/extensions/cornerstone/src/getHangingProtocolModule.ts index 4f2b3ecec..6a1fc8a1c 100644 --- a/extensions/cornerstone/src/getHangingProtocolModule.ts +++ b/extensions/cornerstone/src/getHangingProtocolModule.ts @@ -5,6 +5,7 @@ import { mprAnd3DVolumeViewport } from './hps/mprAnd3DVolumeViewport'; import { only3D } from './hps/only3D'; import { primary3D } from './hps/primary3D'; import { primaryAxial } from './hps/primaryAxial'; +import { frameView } from './hps/frameView'; function getHangingProtocolModule() { return [ @@ -36,6 +37,10 @@ function getHangingProtocolModule() { name: primary3D.id, protocol: primary3D, }, + { + name: frameView.id, + protocol: frameView, + }, ]; } diff --git a/extensions/cornerstone/src/hps/frameView.ts b/extensions/cornerstone/src/hps/frameView.ts new file mode 100644 index 000000000..de93fc602 --- /dev/null +++ b/extensions/cornerstone/src/hps/frameView.ts @@ -0,0 +1,1565 @@ +import { Types } from '@ohif/core'; + +const frameView: Types.HangingProtocol.Protocol = { + id: '@ohif/frameView', + description: 'Frame view for the active series', + name: 'Frame View', + icon: 'tool-stack-scroll', + isPreset: true, + toolGroupIds: ['default'], + displaySetSelectors: { + defaultDisplaySetId: { + seriesMatchingRules: [ + { + attribute: 'numImageFrames', + constraint: { + greaterThan: { value: 16 }, + }, + required: true, + }, + { + attribute: 'isDisplaySetFromUrl', + weight: 10, + constraint: { + equals: true, + }, + }, + ], + }, + }, + defaultViewport: { + viewportOptions: { + viewportType: 'stack', + toolGroupId: 'default', + allowUnmatchedView: true, + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + matchedDisplaySetsIndex: -1, + }, + ], + }, + stages: [ + { + name: 'frameView', + id: '4x4', + viewportStructure: { + layoutType: 'grid', + properties: { + rows: 4, + columns: 4, + }, + }, + viewports: [ + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 0, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 1, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 2, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 3, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 4, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 5, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 6, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 7, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 8, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 9, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 10, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 11, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 12, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 13, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 14, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 15, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + ], + }, + { + name: 'frameView', + id: '3x3', + viewportStructure: { + layoutType: 'grid', + properties: { + rows: 3, + columns: 3, + }, + }, + viewports: [ + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 0, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 1, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 2, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 3, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 4, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 5, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 6, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 7, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 8, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + ], + }, + { + name: 'frameView', + id: '3x2', + viewportStructure: { + layoutType: 'grid', + properties: { + rows: 2, + columns: 3, + }, + }, + viewports: [ + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 0, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 1, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 2, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 3, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 4, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 5, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + ], + }, + { + name: 'frameView', + id: '2x2', + viewportStructure: { + layoutType: 'grid', + properties: { + rows: 2, + columns: 2, + }, + }, + viewports: [ + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 0, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 1, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 2, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 3, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + ], + }, + { + name: 'frameView', + id: '1x3', + viewportStructure: { + layoutType: 'grid', + properties: { + rows: 1, + columns: 3, + }, + }, + viewports: [ + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 0, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 1, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 2, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + ], + }, + { + name: 'frameView', + id: '1x2', + viewportStructure: { + layoutType: 'grid', + properties: { + rows: 1, + columns: 2, + }, + }, + viewports: [ + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 0, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + { + viewportOptions: { + toolGroupId: 'default', + syncGroups: [ + { + type: 'zoompan', + id: 'zoompansync', + source: true, + target: true, + }, + { + type: 'voi', + id: 'wlsync', + source: true, + target: true, + options: { + syncColormap: true, + }, + }, + { + type: 'frameview', + id: 'frameViewSync', + source: true, + target: true, + options: { + viewportIndex: 1, + }, + }, + ], + }, + displaySets: [ + { + id: 'defaultDisplaySetId', + }, + ], + }, + ], + }, + ], +}; + +export { frameView }; diff --git a/extensions/cornerstone/src/index.tsx b/extensions/cornerstone/src/index.tsx index b1a116081..4ea40d202 100644 --- a/extensions/cornerstone/src/index.tsx +++ b/extensions/cornerstone/src/index.tsx @@ -38,6 +38,7 @@ import { ViewportActionCornersProvider } from './contextProviders/ViewportAction import ActiveViewportWindowLevel from './components/ActiveViewportWindowLevel'; import getSOPInstanceAttributes from './utils/measurementServiceMappings/utils/getSOPInstanceAttributes'; import { findNearbyToolData } from './utils/findNearbyToolData'; +import { createFrameViewSynchronizer } from './synchronizers/frameViewSynchronizer'; const { helpers: volumeLoaderHelpers } = csStreamingImageVolumeLoader; const { getDynamicVolumeInfo } = volumeLoaderHelpers ?? {}; @@ -114,6 +115,9 @@ const cornerstoneExtension: Types.Extensions.Extension = { ViewportActionCornersService.REGISTRATION.name, ViewportActionCornersProvider ); + + const { syncGroupService } = servicesManager.services; + syncGroupService.registerCustomSynchronizer('frameview', createFrameViewSynchronizer); return init.call(this, props); }, diff --git a/extensions/cornerstone/src/services/SyncGroupService/SyncGroupService.ts b/extensions/cornerstone/src/services/SyncGroupService/SyncGroupService.ts index 55ba5e6ce..030dd72d8 100644 --- a/extensions/cornerstone/src/services/SyncGroupService/SyncGroupService.ts +++ b/extensions/cornerstone/src/services/SyncGroupService/SyncGroupService.ts @@ -98,6 +98,15 @@ export default class SyncGroupService { return SynchronizerManager.getSynchronizer(id); } + /** + * Registers a custom synchronizer. + * @param id - The id of the synchronizer. + * @param createFunction - The function that creates the synchronizer. + */ + public registerCustomSynchronizer(id: string, createFunction: SyncCreator): void { + this.synchronizerCreators[id] = createFunction; + } + /** * Retrieves an array of synchronizers of a specific type. * @param type - The type of synchronizers to retrieve. diff --git a/extensions/cornerstone/src/synchronizers/frameViewSynchronizer.ts b/extensions/cornerstone/src/synchronizers/frameViewSynchronizer.ts new file mode 100644 index 000000000..8f125d1b6 --- /dev/null +++ b/extensions/cornerstone/src/synchronizers/frameViewSynchronizer.ts @@ -0,0 +1,51 @@ +import { SynchronizerManager, Synchronizer, utilities } from '@cornerstonejs/tools'; +import { EVENTS, getRenderingEngine, Types } from '@cornerstonejs/core'; + +const frameViewSyncCallback = ( + synchronizerInstance: Synchronizer, + sourceViewport: Types.IViewportId, + targetViewport: Types.IViewportId +) => { + const renderingEngine = getRenderingEngine(targetViewport.renderingEngineId); + if (!renderingEngine) { + throw new Error(`No RenderingEngine for Id: ${targetViewport.renderingEngineId}`); + } + const sViewport = renderingEngine.getViewport(sourceViewport.viewportId) as Types.IStackViewport; + + const { viewportIndex: targetViewportIndex } = synchronizerInstance.getOptions( + targetViewport.viewportId + ); + + const { viewportIndex: sourceViewportIndex } = synchronizerInstance.getOptions( + sourceViewport.viewportId + ); + + if (targetViewportIndex === undefined || sourceViewportIndex === undefined) { + throw new Error('No viewportIndex provided'); + } + + const tViewport = renderingEngine.getViewport(targetViewport.viewportId) as Types.IStackViewport; + + const sourceSliceIndex = sViewport.getSliceIndex(); + const sliceDifference = Number(targetViewportIndex) - Number(sourceViewportIndex); + const targetSliceIndex = sourceSliceIndex + sliceDifference; + + if (targetSliceIndex === tViewport.getSliceIndex()) { + return; + } + + utilities.jumpToSlice(tViewport.element, { + imageIndex: targetSliceIndex, + }); +}; + +const createFrameViewSynchronizer = (synchronizerName: string): Synchronizer => { + const synchronizer = SynchronizerManager.createSynchronizer( + synchronizerName, + EVENTS.CAMERA_MODIFIED, + frameViewSyncCallback + ); + return synchronizer; +}; + +export { createFrameViewSynchronizer };