fix(viewport-sync): remember synced viewports bw stack and volume and RENAME StackImageSync to ImageSliceSync (#3849)
This commit is contained in:
parent
2460d28afc
commit
e4a116b074
@ -168,7 +168,7 @@ function OHIFCornerstoneRTViewport(props) {
|
||||
}, [servicesManager, viewportId, rtDisplaySet, rtIsLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
// I'm not sure what is this, since in RT we support Overlapping segmnets
|
||||
// I'm not sure what is this, since in RT we support Overlapping segments
|
||||
// via contours
|
||||
const { unsubscribe } = segmentationService.subscribe(
|
||||
segmentationService.EVENTS.SEGMENTATION_LOADING_COMPLETE,
|
||||
|
||||
@ -175,6 +175,13 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
||||
|
||||
syncGroupService.addViewportToSyncGroup(viewportId, renderingEngineId, syncGroups);
|
||||
|
||||
const synchronizersStore = stateSyncService.getState().synchronizersStore;
|
||||
|
||||
if (synchronizersStore?.[viewportId]?.length) {
|
||||
// If the viewport used to have a synchronizer, re apply it again
|
||||
_rehydrateSynchronizers(synchronizersStore, viewportId, syncGroupService);
|
||||
}
|
||||
|
||||
if (onElementEnabled) {
|
||||
onElementEnabled(evt);
|
||||
}
|
||||
@ -197,13 +204,18 @@ const OHIFCornerstoneViewport = React.memo(props => {
|
||||
return;
|
||||
}
|
||||
|
||||
cleanUpServices(viewportInfo);
|
||||
cornerstoneViewportService.storePresentation({ viewportId });
|
||||
|
||||
// This should be done after the store presentation since synchronizers
|
||||
// will get cleaned up and they need the viewportInfo to be present
|
||||
cleanUpServices(viewportInfo);
|
||||
|
||||
if (onElementDisabled) {
|
||||
onElementDisabled(viewportInfo);
|
||||
}
|
||||
|
||||
cornerstoneViewportService.disableElement(viewportId);
|
||||
|
||||
eventTarget.removeEventListener(Enums.Events.ELEMENT_ENABLED, elementEnabledHandler);
|
||||
};
|
||||
}, []);
|
||||
@ -547,6 +559,58 @@ function _jumpToMeasurement(
|
||||
}
|
||||
}
|
||||
|
||||
function _rehydrateSynchronizers(
|
||||
synchronizersStore: { [key: string]: unknown },
|
||||
viewportId: string,
|
||||
syncGroupService: any
|
||||
) {
|
||||
synchronizersStore[viewportId].forEach(synchronizerObj => {
|
||||
if (!synchronizerObj.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { id, sourceViewports, targetViewports } = synchronizerObj;
|
||||
|
||||
const synchronizer = syncGroupService.getSynchronizer(id);
|
||||
|
||||
if (!synchronizer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sourceViewportInfo = sourceViewports.find(
|
||||
sourceViewport => sourceViewport.viewportId === viewportId
|
||||
);
|
||||
|
||||
const targetViewportInfo = targetViewports.find(
|
||||
targetViewport => targetViewport.viewportId === viewportId
|
||||
);
|
||||
|
||||
const isSourceViewportInSynchronizer = synchronizer
|
||||
.getSourceViewports()
|
||||
.find(sourceViewport => sourceViewport.viewportId === viewportId);
|
||||
|
||||
const isTargetViewportInSynchronizer = synchronizer
|
||||
.getTargetViewports()
|
||||
.find(targetViewport => targetViewport.viewportId === viewportId);
|
||||
|
||||
// if the viewport was previously a source viewport, add it again
|
||||
if (sourceViewportInfo && !isSourceViewportInSynchronizer) {
|
||||
synchronizer.addSource({
|
||||
viewportId: sourceViewportInfo.viewportId,
|
||||
renderingEngineId: sourceViewportInfo.renderingEngineId,
|
||||
});
|
||||
}
|
||||
|
||||
// if the viewport was previously a target viewport, add it again
|
||||
if (targetViewportInfo && !isTargetViewportInSynchronizer) {
|
||||
synchronizer.addTarget({
|
||||
viewportId: targetViewportInfo.viewportId,
|
||||
renderingEngineId: targetViewportInfo.renderingEngineId,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Component displayName
|
||||
OHIFCornerstoneViewport.displayName = 'OHIFCornerstoneViewport';
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ import { vec3, mat4 } from 'gl-matrix';
|
||||
|
||||
import CornerstoneViewportDownloadForm from './utils/CornerstoneViewportDownloadForm';
|
||||
import callInputDialog from './utils/callInputDialog';
|
||||
import toggleStackImageSync from './utils/stackSync/toggleStackImageSync';
|
||||
import toggleImageSliceSync from './utils/imageSliceSync/toggleImageSliceSync';
|
||||
import { getFirstAnnotationSelected } from './utils/measurementServiceMappings/utils/selection';
|
||||
import getActiveViewportEnabledElement from './utils/getActiveViewportEnabledElement';
|
||||
import { CornerstoneServices } from './types';
|
||||
@ -566,8 +566,8 @@ function commandsModule({
|
||||
viewportGridService.setActiveViewportId(viewportIds[nextViewportIndex] as string);
|
||||
},
|
||||
|
||||
toggleStackImageSync: ({ toggledState }) => {
|
||||
toggleStackImageSync({
|
||||
toggleImageSliceSync: ({ toggledState }) => {
|
||||
toggleImageSliceSync({
|
||||
servicesManager,
|
||||
toggledState,
|
||||
});
|
||||
@ -726,8 +726,8 @@ function commandsModule({
|
||||
setViewportColormap: {
|
||||
commandFn: actions.setViewportColormap,
|
||||
},
|
||||
toggleStackImageSync: {
|
||||
commandFn: actions.toggleStackImageSync,
|
||||
toggleImageSliceSync: {
|
||||
commandFn: actions.toggleImageSliceSync,
|
||||
},
|
||||
setSourceViewportForReferenceLinesTool: {
|
||||
commandFn: actions.setSourceViewportForReferenceLinesTool,
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
metaData,
|
||||
volumeLoader,
|
||||
imageLoadPoolManager,
|
||||
getEnabledElement,
|
||||
Settings,
|
||||
utilities as csUtilities,
|
||||
Enums as csEnums,
|
||||
@ -97,6 +98,7 @@ export default async function init({
|
||||
toolbarService,
|
||||
viewportGridService,
|
||||
stateSyncService,
|
||||
syncGroupService,
|
||||
} = servicesManager.services as CornerstoneServices;
|
||||
|
||||
window.services = servicesManager.services;
|
||||
@ -124,6 +126,9 @@ export default async function init({
|
||||
// an OHIFCornerstoneViewport can be redisplayed with the same LUT
|
||||
stateSyncService.register('lutPresentationStore', { clearOnModeExit: true });
|
||||
|
||||
// Stores synchronizers state to be restored
|
||||
stateSyncService.register('synchronizersStore', { clearOnModeExit: true });
|
||||
|
||||
// Stores a map from `positionPresentationId` to a Presentation object so that
|
||||
// an OHIFCornerstoneViewport can be redisplayed with the same position
|
||||
stateSyncService.register('positionPresentationStore', {
|
||||
|
||||
@ -45,7 +45,7 @@ export default class SyncGroupService {
|
||||
[POSITION]: synchronizers.createCameraPositionSynchronizer,
|
||||
[VOI]: synchronizers.createVOISynchronizer,
|
||||
[ZOOMPAN]: synchronizers.createZoomPanSynchronizer,
|
||||
[STACKIMAGE]: synchronizers.createStackImageSynchronizer,
|
||||
[STACKIMAGE]: synchronizers.createImageSliceSynchronizer,
|
||||
};
|
||||
|
||||
constructor(serviceManager: ServicesManager) {
|
||||
@ -74,6 +74,10 @@ export default class SyncGroupService {
|
||||
this.synchronizerCreators[type.toLowerCase()] = creator;
|
||||
}
|
||||
|
||||
public getSynchronizer(id: string): Synchronizer | void {
|
||||
return SynchronizerManager.getSynchronizer(id);
|
||||
}
|
||||
|
||||
protected _getOrCreateSynchronizer(
|
||||
type: string,
|
||||
id: string,
|
||||
@ -121,6 +125,17 @@ export default class SyncGroupService {
|
||||
SynchronizerManager.destroy();
|
||||
}
|
||||
|
||||
public getSynchronizersForViewport(
|
||||
viewportId: string,
|
||||
renderingEngineId: string
|
||||
): Synchronizer[] {
|
||||
return SynchronizerManager.getAllSynchronizers().filter(
|
||||
s =>
|
||||
s.hasSourceViewport(renderingEngineId, viewportId) ||
|
||||
s.hasTargetViewport(renderingEngineId, viewportId)
|
||||
);
|
||||
}
|
||||
|
||||
public removeViewportFromSyncGroup(
|
||||
viewportId: string,
|
||||
renderingEngineId: string,
|
||||
|
||||
@ -178,7 +178,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
}
|
||||
|
||||
public storePresentation({ viewportId }) {
|
||||
const stateSyncService = this.servicesManager.services.stateSyncService;
|
||||
const { stateSyncService, syncGroupService } = this.servicesManager.services;
|
||||
let presentation;
|
||||
try {
|
||||
presentation = this.getPresentation(viewportId);
|
||||
@ -189,7 +189,14 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
if (!presentation || !presentation.presentationIds) {
|
||||
return;
|
||||
}
|
||||
const { lutPresentationStore, positionPresentationStore } = stateSyncService.getState();
|
||||
|
||||
const synchronizers = syncGroupService.getSynchronizersForViewport(
|
||||
viewportId,
|
||||
this.renderingEngine.id
|
||||
);
|
||||
|
||||
const { lutPresentationStore, positionPresentationStore, synchronizersStore } =
|
||||
stateSyncService.getState();
|
||||
const { presentationIds } = presentation;
|
||||
const { lutPresentationId, positionPresentationId } = presentationIds || {};
|
||||
const storeState = {};
|
||||
@ -205,6 +212,22 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
[positionPresentationId]: presentation,
|
||||
};
|
||||
}
|
||||
|
||||
if (synchronizers?.length) {
|
||||
storeState.synchronizersStore = {
|
||||
...synchronizersStore,
|
||||
[viewportId]: synchronizers.map(synchronizer => {
|
||||
const sourceViewports = synchronizer.getSourceViewports();
|
||||
const targetViewports = synchronizer.getTargetViewports();
|
||||
return {
|
||||
id: synchronizer.id,
|
||||
sourceViewports: [...sourceViewports],
|
||||
targetViewports: [...targetViewports],
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
stateSyncService.store(storeState);
|
||||
}
|
||||
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
const STACK_SYNC_NAME = 'stackImageSync';
|
||||
const IMAGE_SLICE_SYNC_NAME = 'IMAGE_SLICE_SYNC';
|
||||
|
||||
export default function toggleStackImageSync({
|
||||
export default function toggleImageSliceSync({
|
||||
toggledState,
|
||||
servicesManager,
|
||||
viewports: providedViewports,
|
||||
}) {
|
||||
if (!toggledState) {
|
||||
return disableSync(STACK_SYNC_NAME, servicesManager);
|
||||
return disableSync(IMAGE_SLICE_SYNC_NAME, servicesManager);
|
||||
}
|
||||
|
||||
const { syncGroupService, viewportGridService, displaySetService, cornerstoneViewportService } =
|
||||
@ -24,7 +24,7 @@ export default function toggleStackImageSync({
|
||||
}
|
||||
syncGroupService.addViewportToSyncGroup(viewportId, viewport.getRenderingEngine().id, {
|
||||
type: 'stackimage',
|
||||
id: STACK_SYNC_NAME,
|
||||
id: IMAGE_SLICE_SYNC_NAME,
|
||||
source: true,
|
||||
target: true,
|
||||
});
|
||||
@ -79,19 +79,19 @@ const moreTools = [
|
||||
'Flip Horizontally'
|
||||
),
|
||||
ToolbarService._createToggleButton(
|
||||
'StackImageSync',
|
||||
'ImageSliceSync',
|
||||
'link',
|
||||
'Stack Image Sync',
|
||||
'Image Slice Sync',
|
||||
[
|
||||
{
|
||||
commandName: 'toggleStackImageSync',
|
||||
commandName: 'toggleImageSliceSync',
|
||||
},
|
||||
],
|
||||
'Enable position synchronization on stack viewports',
|
||||
{
|
||||
listeners: {
|
||||
[EVENTS.STACK_VIEWPORT_NEW_STACK]: {
|
||||
commandName: 'toggleStackImageSync',
|
||||
commandName: 'toggleImageSliceSync',
|
||||
commandOptions: { toggledState: true },
|
||||
},
|
||||
},
|
||||
|
||||
@ -83,19 +83,19 @@ const moreTools = [
|
||||
'Flip Horizontal'
|
||||
),
|
||||
ToolbarService._createToggleButton(
|
||||
'StackImageSync',
|
||||
'ImageSliceSync',
|
||||
'link',
|
||||
'Stack Image Sync',
|
||||
'Image Slice Sync',
|
||||
[
|
||||
{
|
||||
commandName: 'toggleStackImageSync',
|
||||
commandName: 'toggleImageSliceSync',
|
||||
},
|
||||
],
|
||||
'Enable position synchronization on stack viewports',
|
||||
{
|
||||
listeners: {
|
||||
[EVENTS.STACK_VIEWPORT_NEW_STACK]: {
|
||||
commandName: 'toggleStackImageSync',
|
||||
commandName: 'toggleImageSliceSync',
|
||||
commandOptions: { toggledState: true },
|
||||
},
|
||||
},
|
||||
|
||||
@ -57,19 +57,19 @@ const moreToolsMpr = [
|
||||
'Reset'
|
||||
),
|
||||
ToolbarService._createToggleButton(
|
||||
'StackImageSync',
|
||||
'ImageSliceSync',
|
||||
'link',
|
||||
'Stack Image Sync',
|
||||
'Image Slice Sync',
|
||||
[
|
||||
{
|
||||
commandName: 'toggleStackImageSync',
|
||||
commandName: 'toggleImageSliceSync',
|
||||
},
|
||||
],
|
||||
'Enable position synchronization on stack viewports',
|
||||
{
|
||||
listeners: {
|
||||
[EVENTS.STACK_VIEWPORT_NEW_STACK]: {
|
||||
commandName: 'toggleStackImageSync',
|
||||
commandName: 'toggleImageSliceSync',
|
||||
commandOptions: { toggledState: true },
|
||||
},
|
||||
},
|
||||
|
||||
@ -261,9 +261,9 @@ const toolbarButtons = [
|
||||
],
|
||||
'Flip Horizontal'
|
||||
),
|
||||
_createToggleButton('StackImageSync', 'link', 'Stack Image Sync', [
|
||||
_createToggleButton('ImageSliceSync', 'link', 'Stack Image Sync', [
|
||||
{
|
||||
commandName: 'toggleStackImageSync',
|
||||
commandName: 'toggleImageSliceSync',
|
||||
commandOptions: {},
|
||||
context: 'CORNERSTONE',
|
||||
},
|
||||
|
||||
@ -427,7 +427,7 @@ describe('OHIF Cornerstone Toolbar', () => {
|
||||
it('checks if stack sync is preserved on new display set and uses FOR', () => {
|
||||
// Active stack image sync and reference lines
|
||||
cy.get('[data-cy="MoreTools-split-button-secondary"]').click();
|
||||
cy.get('[data-cy="StackImageSync"]').click();
|
||||
cy.get('[data-cy="ImageSliceSync"]').click();
|
||||
// Add reference lines as that sometimes throws an exception
|
||||
cy.get('[data-cy="MoreTools-split-button-secondary"]').click();
|
||||
cy.get('[data-cy="ReferenceLines"]').click();
|
||||
|
||||
@ -65,9 +65,9 @@ describe('OHIF MPR', () => {
|
||||
const viewports = cornerstone.getRenderingEngines()[0].getViewports();
|
||||
|
||||
// The stack viewport still exists after the changes to viewportId and inde
|
||||
const imageData1 = viewports[1].getImageData();
|
||||
const imageData2 = viewports[2].getImageData();
|
||||
const imageData3 = viewports[3].getImageData();
|
||||
const imageData1 = viewports[0].getImageData();
|
||||
const imageData2 = viewports[1].getImageData();
|
||||
const imageData3 = viewports[2].getImageData();
|
||||
|
||||
// for some reason map doesn't work here
|
||||
cy.wrap(imageData1).should('not.be', undefined);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user