2022-07-27 18:39:04 +02:00
|
|
|
import {
|
|
|
|
|
getEnabledElement,
|
|
|
|
|
StackViewport,
|
|
|
|
|
utilities as csUtils,
|
|
|
|
|
} from '@cornerstonejs/core';
|
|
|
|
|
import {
|
|
|
|
|
ToolGroupManager,
|
|
|
|
|
Enums,
|
2022-11-16 20:19:52 +01:00
|
|
|
utilities as cstUtils,
|
|
|
|
|
segmentation as cstSegmentation,
|
|
|
|
|
ReferenceLinesTool,
|
|
|
|
|
synchronizers as cstSynchronizers,
|
2022-07-27 18:39:04 +02:00
|
|
|
} from '@cornerstonejs/tools';
|
2022-09-21 05:40:26 +02:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
import CornerstoneViewportDownloadForm from './utils/CornerstoneViewportDownloadForm';
|
|
|
|
|
|
|
|
|
|
import { getEnabledElement as OHIFgetEnabledElement } from './state';
|
|
|
|
|
import callInputDialog from './utils/callInputDialog';
|
|
|
|
|
import { setColormap } from './utils/colormap/transferFunctionHelpers';
|
2022-11-16 20:19:52 +01:00
|
|
|
import getProtocolViewportStructureFromGridViewports from './utils/getProtocolViewportStructureFromGridViewports';
|
|
|
|
|
import removeToolGroupSegmentationRepresentations from './utils/removeToolGroupSegmentationRepresentations';
|
|
|
|
|
import calculateViewportRegistrations from './utils/calculateViewportRegistrations';
|
|
|
|
|
|
|
|
|
|
const MPR_TOOLGROUP_ID = 'mpr';
|
|
|
|
|
|
|
|
|
|
// [ {
|
|
|
|
|
// synchronizerId: string,
|
|
|
|
|
// viewports: [ { viewportId: number, renderingEngineId: string, index: number } , ...]
|
|
|
|
|
// ]}
|
|
|
|
|
let STACK_IMAGE_SYNC_GROUPS_INFO = [];
|
2022-07-27 18:39:04 +02:00
|
|
|
|
|
|
|
|
const commandsModule = ({ servicesManager }) => {
|
|
|
|
|
const {
|
|
|
|
|
ViewportGridService,
|
|
|
|
|
ToolGroupService,
|
2022-11-16 20:19:52 +01:00
|
|
|
DisplaySetService,
|
|
|
|
|
SyncGroupService,
|
2022-07-27 18:39:04 +02:00
|
|
|
CineService,
|
|
|
|
|
ToolBarService,
|
|
|
|
|
UIDialogService,
|
|
|
|
|
CornerstoneViewportService,
|
2022-09-21 05:40:26 +02:00
|
|
|
HangingProtocolService,
|
|
|
|
|
UINotificationService,
|
2022-07-27 18:39:04 +02:00
|
|
|
} = servicesManager.services;
|
|
|
|
|
|
|
|
|
|
function _getActiveViewportEnabledElement() {
|
|
|
|
|
const { activeViewportIndex } = ViewportGridService.getState();
|
|
|
|
|
const { element } = OHIFgetEnabledElement(activeViewportIndex) || {};
|
|
|
|
|
const enabledElement = getEnabledElement(element);
|
|
|
|
|
return enabledElement;
|
|
|
|
|
}
|
2019-12-02 20:02:35 +01:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
function _getToolGroup(toolGroupId) {
|
|
|
|
|
let toolGroupIdToUse = toolGroupId;
|
2019-06-05 21:57:00 +02:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
if (!toolGroupIdToUse) {
|
|
|
|
|
// Use the active viewport's tool group if no tool group id is provided
|
|
|
|
|
const enabledElement = _getActiveViewportEnabledElement();
|
2020-04-09 12:21:43 +02:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
if (!enabledElement) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-05-28 20:32:48 +02:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
const { renderingEngineId, viewportId } = enabledElement;
|
|
|
|
|
const toolGroup = ToolGroupManager.getToolGroupForViewport(
|
|
|
|
|
viewportId,
|
|
|
|
|
renderingEngineId
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!toolGroup) {
|
|
|
|
|
console.warn(
|
|
|
|
|
'No tool group found for viewportId:',
|
|
|
|
|
viewportId,
|
|
|
|
|
'and renderingEngineId:',
|
|
|
|
|
renderingEngineId
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-05-28 20:32:48 +02:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
toolGroupIdToUse = toolGroup.id;
|
|
|
|
|
}
|
2021-05-28 20:32:48 +02:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
const toolGroup = ToolGroupService.getToolGroup(toolGroupIdToUse);
|
|
|
|
|
return toolGroup;
|
|
|
|
|
}
|
2021-05-28 20:32:48 +02:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
const actions = {
|
|
|
|
|
getActiveViewportEnabledElement: () => {
|
|
|
|
|
return _getActiveViewportEnabledElement();
|
|
|
|
|
},
|
|
|
|
|
setViewportActive: ({ viewportId }) => {
|
|
|
|
|
const viewportInfo = CornerstoneViewportService.getViewportInfo(
|
|
|
|
|
viewportId
|
|
|
|
|
);
|
|
|
|
|
if (!viewportInfo) {
|
|
|
|
|
console.warn('No viewport found for viewportId:', viewportId);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-05-28 20:32:48 +02:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
const viewportIndex = viewportInfo.getViewportIndex();
|
|
|
|
|
ViewportGridService.setActiveViewportIndex(viewportIndex);
|
|
|
|
|
},
|
|
|
|
|
arrowTextCallback: ({ callback, data }) => {
|
|
|
|
|
callInputDialog(UIDialogService, data, callback);
|
|
|
|
|
},
|
|
|
|
|
toggleCine: () => {
|
|
|
|
|
const { viewports } = ViewportGridService.getState();
|
|
|
|
|
const { isCineEnabled } = CineService.getState();
|
|
|
|
|
CineService.setIsCineEnabled(!isCineEnabled);
|
|
|
|
|
ToolBarService.setButton('Cine', { props: { isActive: !isCineEnabled } });
|
|
|
|
|
viewports.forEach((_, index) =>
|
|
|
|
|
CineService.setCine({ id: index, isPlaying: false })
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
setWindowLevel({ window, level, toolGroupId }) {
|
|
|
|
|
// convert to numbers
|
|
|
|
|
const windowWidthNum = Number(window);
|
|
|
|
|
const windowCenterNum = Number(level);
|
2021-05-28 20:32:48 +02:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
const { viewportId } = _getActiveViewportEnabledElement();
|
|
|
|
|
const viewportToolGroupId = ToolGroupService.getToolGroupForViewport(
|
|
|
|
|
viewportId
|
|
|
|
|
);
|
2020-06-26 19:51:27 +02:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
if (toolGroupId && toolGroupId !== viewportToolGroupId) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-06-26 20:22:45 +02:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
// get actor from the viewport
|
|
|
|
|
const renderingEngine = CornerstoneViewportService.getRenderingEngine();
|
|
|
|
|
const viewport = renderingEngine.getViewport(viewportId);
|
|
|
|
|
|
2022-08-17 21:13:59 +02:00
|
|
|
const { lower, upper } = csUtils.windowLevel.toLowHighRange(
|
|
|
|
|
windowWidthNum,
|
|
|
|
|
windowCenterNum
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
viewport.setProperties({
|
|
|
|
|
voiRange: {
|
|
|
|
|
upper,
|
|
|
|
|
lower,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
viewport.render();
|
2019-12-02 20:02:35 +01:00
|
|
|
},
|
2022-11-16 20:19:52 +01:00
|
|
|
setToolActive: ({ toolName, toolGroupId = null }) => {
|
|
|
|
|
if (toolName === 'Crosshairs') {
|
|
|
|
|
const activeViewportToolGroup = _getToolGroup(null);
|
|
|
|
|
|
|
|
|
|
if (!activeViewportToolGroup._toolInstances.Crosshairs) {
|
|
|
|
|
UINotificationService.show({
|
|
|
|
|
title: 'Crosshairs',
|
|
|
|
|
message:
|
|
|
|
|
'You need to be in a MPR view to use Crosshairs. Click on MPR button in the toolbar to activate it.',
|
|
|
|
|
type: 'info',
|
|
|
|
|
duration: 3000,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
throw new Error('Crosshairs tool is not available in this viewport');
|
|
|
|
|
}
|
2019-12-02 20:02:35 +01:00
|
|
|
}
|
|
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
const toolGroup = _getToolGroup(toolGroupId);
|
|
|
|
|
|
|
|
|
|
if (!toolGroup) {
|
|
|
|
|
console.warn('No tool group found for toolGroupId:', toolGroupId);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Todo: we need to check if the viewports of the toolGroup is actually
|
|
|
|
|
// parts of the ViewportGrid's viewports, if not we return
|
|
|
|
|
|
|
|
|
|
const { viewports } = ViewportGridService.getState() || {
|
|
|
|
|
viewports: [],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// iterate over all viewports and set the tool active for the
|
|
|
|
|
// viewports that belong to the toolGroup
|
|
|
|
|
for (let index = 0; index < viewports.length; index++) {
|
|
|
|
|
const ohifEnabledElement = OHIFgetEnabledElement(index);
|
|
|
|
|
|
|
|
|
|
if (!ohifEnabledElement) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const viewport = getEnabledElement(ohifEnabledElement.element);
|
|
|
|
|
|
|
|
|
|
if (!viewport) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the current active tool and set it to be passive
|
|
|
|
|
const activeTool = toolGroup.getActivePrimaryMouseButtonTool();
|
|
|
|
|
|
|
|
|
|
if (activeTool) {
|
|
|
|
|
toolGroup.setToolPassive(activeTool);
|
2019-06-25 00:04:26 +02:00
|
|
|
}
|
2022-07-27 18:39:04 +02:00
|
|
|
|
|
|
|
|
// Set the new toolName to be active
|
|
|
|
|
toolGroup.setToolActive(toolName, {
|
|
|
|
|
bindings: [{ mouseButton: Enums.MouseBindings.Primary }],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return;
|
2019-12-02 20:02:35 +01:00
|
|
|
}
|
|
|
|
|
},
|
2022-07-27 18:39:04 +02:00
|
|
|
showDownloadViewportModal: () => {
|
|
|
|
|
const { activeViewportIndex } = ViewportGridService.getState();
|
|
|
|
|
const { UIModalService } = servicesManager.services;
|
2019-12-02 20:02:35 +01:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
if (UIModalService) {
|
|
|
|
|
UIModalService.show({
|
|
|
|
|
content: CornerstoneViewportDownloadForm,
|
|
|
|
|
title: 'Download High Quality Image',
|
|
|
|
|
contentProps: {
|
|
|
|
|
activeViewportIndex,
|
|
|
|
|
onClose: UIModalService.hide,
|
|
|
|
|
CornerstoneViewportService,
|
|
|
|
|
},
|
|
|
|
|
});
|
2019-12-02 20:02:35 +01:00
|
|
|
}
|
|
|
|
|
},
|
2022-07-27 18:39:04 +02:00
|
|
|
rotateViewport: ({ rotation }) => {
|
|
|
|
|
const enabledElement = _getActiveViewportEnabledElement();
|
|
|
|
|
if (!enabledElement) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-05-28 20:32:48 +02:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
const { viewport } = enabledElement;
|
2021-05-28 20:32:48 +02:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
if (viewport instanceof StackViewport) {
|
|
|
|
|
const { rotation: currentRotation } = viewport.getProperties();
|
|
|
|
|
const newRotation = (currentRotation + rotation) % 360;
|
|
|
|
|
viewport.setProperties({ rotation: newRotation });
|
|
|
|
|
viewport.render();
|
2021-05-28 20:32:48 +02:00
|
|
|
}
|
2022-07-27 18:39:04 +02:00
|
|
|
},
|
|
|
|
|
flipViewportHorizontal: () => {
|
|
|
|
|
const enabledElement = _getActiveViewportEnabledElement();
|
2021-05-28 20:32:48 +02:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
if (!enabledElement) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { viewport } = enabledElement;
|
|
|
|
|
|
|
|
|
|
if (viewport instanceof StackViewport) {
|
|
|
|
|
const { flipHorizontal } = viewport.getCamera();
|
|
|
|
|
viewport.setCamera({ flipHorizontal: !flipHorizontal });
|
|
|
|
|
viewport.render();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
flipViewportVertical: () => {
|
|
|
|
|
const enabledElement = _getActiveViewportEnabledElement();
|
|
|
|
|
|
|
|
|
|
if (!enabledElement) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { viewport } = enabledElement;
|
|
|
|
|
|
|
|
|
|
if (viewport instanceof StackViewport) {
|
|
|
|
|
const { flipVertical } = viewport.getCamera();
|
|
|
|
|
viewport.setCamera({ flipVertical: !flipVertical });
|
|
|
|
|
viewport.render();
|
|
|
|
|
}
|
2021-05-28 20:32:48 +02:00
|
|
|
},
|
|
|
|
|
invertViewport: ({ element }) => {
|
|
|
|
|
let enabledElement;
|
|
|
|
|
|
|
|
|
|
if (element === undefined) {
|
2022-07-27 18:39:04 +02:00
|
|
|
enabledElement = _getActiveViewportEnabledElement();
|
2021-05-28 20:32:48 +02:00
|
|
|
} else {
|
|
|
|
|
enabledElement = element;
|
|
|
|
|
}
|
2019-06-25 00:04:26 +02:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
if (!enabledElement) {
|
|
|
|
|
return;
|
2019-12-02 20:02:35 +01:00
|
|
|
}
|
2020-07-10 21:00:10 +02:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
const { viewport } = enabledElement;
|
2020-07-10 21:00:10 +02:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
if (viewport instanceof StackViewport) {
|
|
|
|
|
const { invert } = viewport.getProperties();
|
|
|
|
|
viewport.setProperties({ invert: !invert });
|
|
|
|
|
viewport.render();
|
2020-07-10 21:00:10 +02:00
|
|
|
}
|
|
|
|
|
},
|
2022-07-27 18:39:04 +02:00
|
|
|
resetViewport: () => {
|
|
|
|
|
const enabledElement = _getActiveViewportEnabledElement();
|
|
|
|
|
|
|
|
|
|
if (!enabledElement) {
|
|
|
|
|
return;
|
2019-12-02 20:02:35 +01:00
|
|
|
}
|
2020-09-15 03:18:46 +02:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
const { viewport } = enabledElement;
|
2020-09-15 03:18:46 +02:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
if (viewport instanceof StackViewport) {
|
|
|
|
|
viewport.resetProperties();
|
|
|
|
|
viewport.resetCamera();
|
2022-11-16 20:19:52 +01:00
|
|
|
} else {
|
|
|
|
|
// Todo: add reset properties for volume viewport
|
|
|
|
|
viewport.resetCamera();
|
2020-09-15 03:18:46 +02:00
|
|
|
}
|
2022-11-16 20:19:52 +01:00
|
|
|
|
|
|
|
|
viewport.render();
|
2019-12-02 20:02:35 +01:00
|
|
|
},
|
2022-07-27 18:39:04 +02:00
|
|
|
scaleViewport: ({ direction }) => {
|
|
|
|
|
const enabledElement = _getActiveViewportEnabledElement();
|
|
|
|
|
const scaleFactor = direction > 0 ? 0.9 : 1.1;
|
|
|
|
|
|
|
|
|
|
if (!enabledElement) {
|
2019-12-02 20:02:35 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2022-07-27 18:39:04 +02:00
|
|
|
const { viewport } = enabledElement;
|
2019-12-02 20:02:35 +01:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
if (viewport instanceof StackViewport) {
|
|
|
|
|
if (direction) {
|
|
|
|
|
const { parallelScale } = viewport.getCamera();
|
|
|
|
|
viewport.setCamera({ parallelScale: parallelScale * scaleFactor });
|
|
|
|
|
viewport.render();
|
|
|
|
|
} else {
|
|
|
|
|
viewport.resetCamera();
|
|
|
|
|
viewport.render();
|
|
|
|
|
}
|
2019-12-02 20:02:35 +01:00
|
|
|
}
|
2022-07-27 18:39:04 +02:00
|
|
|
},
|
|
|
|
|
scroll: ({ direction }) => {
|
|
|
|
|
const enabledElement = _getActiveViewportEnabledElement();
|
2019-12-02 20:02:35 +01:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
if (!enabledElement) {
|
2019-12-02 20:02:35 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
const { viewport } = enabledElement;
|
|
|
|
|
const options = { delta: direction };
|
2019-12-02 20:02:35 +01:00
|
|
|
|
2022-11-16 20:19:52 +01:00
|
|
|
cstUtils.scroll(viewport, options);
|
2022-07-27 18:39:04 +02:00
|
|
|
},
|
|
|
|
|
setViewportColormap: ({
|
|
|
|
|
viewportIndex,
|
|
|
|
|
displaySetInstanceUID,
|
|
|
|
|
colormap,
|
|
|
|
|
immediate = false,
|
|
|
|
|
}) => {
|
|
|
|
|
const viewport = CornerstoneViewportService.getCornerstoneViewportByIndex(
|
|
|
|
|
viewportIndex
|
|
|
|
|
);
|
2019-12-09 18:47:23 +01:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
const actorEntries = viewport.getActors();
|
2019-12-09 18:47:23 +01:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
const actorEntry = actorEntries.find(actorEntry => {
|
2022-11-16 20:19:52 +01:00
|
|
|
return actorEntry.uid.includes(displaySetInstanceUID);
|
2019-06-25 00:04:26 +02:00
|
|
|
});
|
2019-12-09 18:47:23 +01:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
const { actor: volumeActor } = actorEntry;
|
2020-02-20 22:25:26 +01:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
setColormap(volumeActor, colormap);
|
2020-03-09 20:03:23 +01:00
|
|
|
|
2022-07-27 18:39:04 +02:00
|
|
|
if (immediate) {
|
|
|
|
|
viewport.render();
|
2020-02-20 22:25:26 +01:00
|
|
|
}
|
|
|
|
|
},
|
2022-09-09 04:02:20 +02:00
|
|
|
incrementActiveViewport: () => {
|
|
|
|
|
const { activeViewportIndex, viewports } = ViewportGridService.getState();
|
|
|
|
|
const nextViewportIndex = (activeViewportIndex + 1) % viewports.length;
|
|
|
|
|
ViewportGridService.setActiveViewportIndex(nextViewportIndex);
|
|
|
|
|
},
|
|
|
|
|
decrementActiveViewport: () => {
|
|
|
|
|
const { activeViewportIndex, viewports } = ViewportGridService.getState();
|
|
|
|
|
const nextViewportIndex =
|
|
|
|
|
(activeViewportIndex - 1 + viewports.length) % viewports.length;
|
|
|
|
|
ViewportGridService.setActiveViewportIndex(nextViewportIndex);
|
|
|
|
|
},
|
2022-09-21 05:40:26 +02:00
|
|
|
setHangingProtocol: ({ protocolId }) => {
|
|
|
|
|
HangingProtocolService.setProtocol(protocolId);
|
|
|
|
|
},
|
2022-11-16 20:19:52 +01:00
|
|
|
toggleMPR: ({ toggledState }) => {
|
|
|
|
|
const { activeViewportIndex, viewports } = ViewportGridService.getState();
|
|
|
|
|
const viewportDisplaySetInstanceUIDs =
|
|
|
|
|
viewports[activeViewportIndex].displaySetInstanceUIDs;
|
|
|
|
|
|
|
|
|
|
const errorCallback = error => {
|
|
|
|
|
UINotificationService.show({
|
|
|
|
|
title: 'Multiplanar reconstruction (MPR) ',
|
|
|
|
|
message:
|
|
|
|
|
'Cannot create MPR for this DisplaySet since it is not reconstructable.',
|
|
|
|
|
type: 'info',
|
|
|
|
|
duration: 3000,
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const cacheId = 'beforeMPR';
|
|
|
|
|
if (toggledState) {
|
|
|
|
|
ViewportGridService.setCachedLayout({
|
|
|
|
|
cacheId,
|
|
|
|
|
cachedLayout: ViewportGridService.getState(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const matchDetails = {
|
|
|
|
|
displaySetInstanceUIDs: viewportDisplaySetInstanceUIDs,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
HangingProtocolService.setProtocol(
|
|
|
|
|
MPR_TOOLGROUP_ID,
|
|
|
|
|
matchDetails,
|
|
|
|
|
errorCallback
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { cachedLayout } = ViewportGridService.getState();
|
|
|
|
|
|
|
|
|
|
if (!cachedLayout || !cachedLayout[cacheId]) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { viewports: cachedViewports, numRows, numCols } = cachedLayout[
|
|
|
|
|
cacheId
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// Todo: The following assumes that when turning off MPR we are applying the default
|
|
|
|
|
// protocol which might not be the one that was used before MPR was turned on
|
|
|
|
|
// In order to properly implement this logic, we should modify the hanging protocol
|
|
|
|
|
// upon layout change with layout selector, and cache and restore it when turning
|
|
|
|
|
// MPR on and off
|
|
|
|
|
const viewportStructure = getProtocolViewportStructureFromGridViewports({
|
|
|
|
|
viewports: cachedViewports,
|
|
|
|
|
numRows,
|
|
|
|
|
numCols,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const viewportSpecificMatch = cachedViewports.reduce(
|
|
|
|
|
(acc, viewport, index) => {
|
|
|
|
|
const {
|
|
|
|
|
displaySetInstanceUIDs,
|
|
|
|
|
viewportOptions,
|
|
|
|
|
displaySetOptions,
|
|
|
|
|
} = viewport;
|
|
|
|
|
|
|
|
|
|
acc[index] = {
|
|
|
|
|
displaySetInstanceUIDs,
|
|
|
|
|
viewportOptions,
|
|
|
|
|
displaySetOptions,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return acc;
|
|
|
|
|
},
|
|
|
|
|
{}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const defaultProtocol = HangingProtocolService.getProtocolById('default');
|
|
|
|
|
|
|
|
|
|
// Todo: this assumes there is only one stage in the default protocol
|
|
|
|
|
const defaultProtocolStage = defaultProtocol.stages[0];
|
|
|
|
|
defaultProtocolStage.viewportStructure = viewportStructure;
|
|
|
|
|
|
|
|
|
|
const { primaryToolId } = ToolBarService.state;
|
|
|
|
|
const mprToolGroup = _getToolGroup(MPR_TOOLGROUP_ID);
|
|
|
|
|
// turn off crosshairs if it is on
|
|
|
|
|
if (
|
|
|
|
|
primaryToolId === 'Crosshairs' ||
|
|
|
|
|
mprToolGroup.getToolInstance('Crosshairs')?.mode ===
|
|
|
|
|
Enums.ToolModes.Active
|
|
|
|
|
) {
|
|
|
|
|
const toolGroup = _getToolGroup(MPR_TOOLGROUP_ID);
|
|
|
|
|
toolGroup.setToolDisabled('Crosshairs');
|
|
|
|
|
ToolBarService.recordInteraction({
|
|
|
|
|
groupId: 'WindowLevel',
|
|
|
|
|
itemId: 'WindowLevel',
|
|
|
|
|
interactionType: 'tool',
|
|
|
|
|
commands: [
|
|
|
|
|
{
|
|
|
|
|
commandName: 'setToolActive',
|
|
|
|
|
commandOptions: {
|
|
|
|
|
toolName: 'WindowLevel',
|
|
|
|
|
},
|
|
|
|
|
context: 'CORNERSTONE',
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// clear segmentations if they exist
|
|
|
|
|
removeToolGroupSegmentationRepresentations(MPR_TOOLGROUP_ID);
|
|
|
|
|
|
|
|
|
|
HangingProtocolService.setProtocol(
|
|
|
|
|
'default',
|
|
|
|
|
viewportSpecificMatch,
|
|
|
|
|
error => {
|
|
|
|
|
UINotificationService.show({
|
|
|
|
|
title: 'Multiplanar reconstruction (MPR) ',
|
|
|
|
|
message:
|
|
|
|
|
'Something went wrong while trying to restore the previous layout.',
|
|
|
|
|
type: 'info',
|
|
|
|
|
duration: 3000,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
toggleStackImageSync: ({ toggledState }) => {
|
|
|
|
|
if (!toggledState) {
|
|
|
|
|
STACK_IMAGE_SYNC_GROUPS_INFO.forEach(syncGroupInfo => {
|
|
|
|
|
const { viewports, synchronizerId } = syncGroupInfo;
|
|
|
|
|
|
|
|
|
|
viewports.forEach(({ viewportId, renderingEngineId }) => {
|
|
|
|
|
SyncGroupService.removeViewportFromSyncGroup(
|
|
|
|
|
viewportId,
|
|
|
|
|
renderingEngineId,
|
|
|
|
|
synchronizerId
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
STACK_IMAGE_SYNC_GROUPS_INFO = [];
|
|
|
|
|
|
|
|
|
|
// create synchronization groups and add viewports
|
|
|
|
|
let { viewports } = ViewportGridService.getState();
|
|
|
|
|
|
|
|
|
|
// filter empty viewports
|
|
|
|
|
viewports = viewports.filter(
|
|
|
|
|
viewport =>
|
|
|
|
|
viewport.displaySetInstanceUIDs &&
|
|
|
|
|
viewport.displaySetInstanceUIDs.length
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// filter reconstructable viewports
|
|
|
|
|
viewports = viewports.filter(viewport => {
|
|
|
|
|
const { displaySetInstanceUIDs } = viewport;
|
|
|
|
|
|
|
|
|
|
for (const displaySetInstanceUID of displaySetInstanceUIDs) {
|
|
|
|
|
const displaySet = DisplaySetService.getDisplaySetByUID(
|
|
|
|
|
displaySetInstanceUID
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (displaySet && displaySet.isReconstructable) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const viewportsByOrientation = viewports.reduce((acc, viewport) => {
|
|
|
|
|
const { viewportId, viewportType } = viewport.viewportOptions;
|
|
|
|
|
|
|
|
|
|
if (viewportType !== 'stack') {
|
|
|
|
|
console.warn('Viewport is not a stack, cannot sync images yet');
|
|
|
|
|
return acc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { element } = CornerstoneViewportService.getViewportInfo(
|
|
|
|
|
viewportId
|
|
|
|
|
);
|
|
|
|
|
const { viewport: csViewport, renderingEngineId } = getEnabledElement(
|
|
|
|
|
element
|
|
|
|
|
);
|
|
|
|
|
const { viewPlaneNormal } = csViewport.getCamera();
|
|
|
|
|
|
|
|
|
|
// Should we round here? I guess so, but not sure how much precision we need
|
|
|
|
|
const orientation = viewPlaneNormal.map(v => Math.round(v)).join(',');
|
|
|
|
|
|
|
|
|
|
if (!acc[orientation]) {
|
|
|
|
|
acc[orientation] = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
acc[orientation].push({ viewportId, renderingEngineId });
|
|
|
|
|
|
|
|
|
|
return acc;
|
|
|
|
|
}, {});
|
|
|
|
|
|
|
|
|
|
// create synchronizer for each group
|
|
|
|
|
Object.values(viewportsByOrientation).map(viewports => {
|
|
|
|
|
let synchronizerId = viewports
|
|
|
|
|
.map(({ viewportId }) => viewportId)
|
|
|
|
|
.join(',');
|
|
|
|
|
|
|
|
|
|
synchronizerId = `imageSync_${synchronizerId}`;
|
|
|
|
|
|
|
|
|
|
calculateViewportRegistrations(viewports);
|
|
|
|
|
|
|
|
|
|
viewports.forEach(({ viewportId, renderingEngineId }) => {
|
|
|
|
|
SyncGroupService.addViewportToSyncGroup(
|
|
|
|
|
viewportId,
|
|
|
|
|
renderingEngineId,
|
|
|
|
|
{
|
|
|
|
|
type: 'stackimage',
|
|
|
|
|
id: synchronizerId,
|
|
|
|
|
source: true,
|
|
|
|
|
target: true,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
STACK_IMAGE_SYNC_GROUPS_INFO.push({
|
|
|
|
|
synchronizerId,
|
|
|
|
|
viewports,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
toggleReferenceLines: ({ toggledState }) => {
|
|
|
|
|
const { activeViewportIndex } = ViewportGridService.getState();
|
|
|
|
|
const viewportInfo = CornerstoneViewportService.getViewportInfoByIndex(
|
|
|
|
|
activeViewportIndex
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const viewportId = viewportInfo.getViewportId();
|
|
|
|
|
const toolGroup = ToolGroupService.getToolGroupForViewport(viewportId);
|
|
|
|
|
|
|
|
|
|
if (!toggledState) {
|
|
|
|
|
toolGroup.setToolDisabled(ReferenceLinesTool.toolName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
toolGroup.setToolConfiguration(
|
|
|
|
|
ReferenceLinesTool.toolName,
|
|
|
|
|
{
|
|
|
|
|
sourceViewportId: viewportId,
|
|
|
|
|
},
|
|
|
|
|
true // overwrite
|
|
|
|
|
);
|
|
|
|
|
toolGroup.setToolEnabled(ReferenceLinesTool.toolName);
|
|
|
|
|
},
|
2019-12-02 20:02:35 +01:00
|
|
|
};
|
2019-05-31 22:25:10 +02:00
|
|
|
|
2019-12-02 20:02:35 +01:00
|
|
|
const definitions = {
|
2022-07-27 18:39:04 +02:00
|
|
|
setWindowLevel: {
|
|
|
|
|
commandFn: actions.setWindowLevel,
|
2019-12-09 18:47:23 +01:00
|
|
|
storeContexts: [],
|
|
|
|
|
options: {},
|
|
|
|
|
},
|
2022-07-27 18:39:04 +02:00
|
|
|
setToolActive: {
|
|
|
|
|
commandFn: actions.setToolActive,
|
2020-06-15 05:43:31 +02:00
|
|
|
storeContexts: [],
|
2019-12-02 20:02:35 +01:00
|
|
|
options: {},
|
|
|
|
|
},
|
2022-07-27 18:39:04 +02:00
|
|
|
toggleCrosshairs: {
|
|
|
|
|
commandFn: actions.toggleCrosshairs,
|
2020-06-26 20:22:45 +02:00
|
|
|
storeContexts: [],
|
2019-12-02 20:02:35 +01:00
|
|
|
options: {},
|
|
|
|
|
},
|
|
|
|
|
rotateViewportCW: {
|
|
|
|
|
commandFn: actions.rotateViewport,
|
2020-06-17 14:50:11 +02:00
|
|
|
storeContexts: [],
|
2019-12-02 20:02:35 +01:00
|
|
|
options: { rotation: 90 },
|
|
|
|
|
},
|
|
|
|
|
rotateViewportCCW: {
|
|
|
|
|
commandFn: actions.rotateViewport,
|
2020-06-26 20:22:45 +02:00
|
|
|
storeContexts: [],
|
2019-12-02 20:02:35 +01:00
|
|
|
options: { rotation: -90 },
|
|
|
|
|
},
|
2022-09-09 04:02:20 +02:00
|
|
|
incrementActiveViewport: {
|
|
|
|
|
commandFn: actions.incrementActiveViewport,
|
|
|
|
|
storeContexts: [],
|
|
|
|
|
},
|
|
|
|
|
decrementActiveViewport: {
|
|
|
|
|
commandFn: actions.decrementActiveViewport,
|
|
|
|
|
storeContexts: [],
|
|
|
|
|
},
|
2022-07-27 18:39:04 +02:00
|
|
|
flipViewportHorizontal: {
|
|
|
|
|
commandFn: actions.flipViewportHorizontal,
|
2020-06-17 14:50:11 +02:00
|
|
|
storeContexts: [],
|
2019-12-02 20:02:35 +01:00
|
|
|
options: {},
|
|
|
|
|
},
|
2022-07-27 18:39:04 +02:00
|
|
|
flipViewportVertical: {
|
|
|
|
|
commandFn: actions.flipViewportVertical,
|
2020-07-10 21:00:10 +02:00
|
|
|
storeContexts: [],
|
|
|
|
|
options: {},
|
|
|
|
|
},
|
2022-07-27 18:39:04 +02:00
|
|
|
invertViewport: {
|
|
|
|
|
commandFn: actions.invertViewport,
|
2020-06-26 20:22:45 +02:00
|
|
|
storeContexts: [],
|
2019-12-02 20:02:35 +01:00
|
|
|
options: {},
|
|
|
|
|
},
|
2022-07-27 18:39:04 +02:00
|
|
|
resetViewport: {
|
|
|
|
|
commandFn: actions.resetViewport,
|
2020-06-17 14:50:11 +02:00
|
|
|
storeContexts: [],
|
2019-12-02 20:02:35 +01:00
|
|
|
options: {},
|
|
|
|
|
},
|
|
|
|
|
scaleUpViewport: {
|
|
|
|
|
commandFn: actions.scaleViewport,
|
2020-06-26 20:22:45 +02:00
|
|
|
storeContexts: [],
|
2019-12-02 20:02:35 +01:00
|
|
|
options: { direction: 1 },
|
|
|
|
|
},
|
|
|
|
|
scaleDownViewport: {
|
|
|
|
|
commandFn: actions.scaleViewport,
|
2020-06-26 20:22:45 +02:00
|
|
|
storeContexts: [],
|
2019-12-02 20:02:35 +01:00
|
|
|
options: { direction: -1 },
|
|
|
|
|
},
|
|
|
|
|
fitViewportToWindow: {
|
|
|
|
|
commandFn: actions.scaleViewport,
|
2020-06-26 20:22:45 +02:00
|
|
|
storeContexts: [],
|
2019-12-02 20:02:35 +01:00
|
|
|
options: { direction: 0 },
|
|
|
|
|
},
|
2022-07-27 18:39:04 +02:00
|
|
|
nextImage: {
|
|
|
|
|
commandFn: actions.scroll,
|
|
|
|
|
storeContexts: [],
|
|
|
|
|
options: { direction: 1 },
|
|
|
|
|
},
|
|
|
|
|
previousImage: {
|
|
|
|
|
commandFn: actions.scroll,
|
|
|
|
|
storeContexts: [],
|
|
|
|
|
options: { direction: -1 },
|
|
|
|
|
},
|
|
|
|
|
showDownloadViewportModal: {
|
|
|
|
|
commandFn: actions.showDownloadViewportModal,
|
2020-06-17 14:50:11 +02:00
|
|
|
storeContexts: [],
|
2019-12-02 20:02:35 +01:00
|
|
|
options: {},
|
|
|
|
|
},
|
2022-07-27 18:39:04 +02:00
|
|
|
toggleCine: {
|
|
|
|
|
commandFn: actions.toggleCine,
|
2020-06-26 20:22:45 +02:00
|
|
|
storeContexts: [],
|
2019-12-02 20:02:35 +01:00
|
|
|
options: {},
|
|
|
|
|
},
|
2022-07-27 18:39:04 +02:00
|
|
|
arrowTextCallback: {
|
|
|
|
|
commandFn: actions.arrowTextCallback,
|
2020-06-26 20:22:45 +02:00
|
|
|
storeContexts: [],
|
2019-12-02 20:02:35 +01:00
|
|
|
options: {},
|
|
|
|
|
},
|
2022-07-27 18:39:04 +02:00
|
|
|
setViewportActive: {
|
|
|
|
|
commandFn: actions.setViewportActive,
|
2020-06-26 20:22:45 +02:00
|
|
|
storeContexts: [],
|
2019-12-02 20:02:35 +01:00
|
|
|
options: {},
|
|
|
|
|
},
|
2022-11-16 20:19:52 +01:00
|
|
|
setViewportColormap: {
|
|
|
|
|
commandFn: actions.setViewportColormap,
|
2019-12-02 20:02:35 +01:00
|
|
|
storeContexts: [],
|
|
|
|
|
options: {},
|
|
|
|
|
},
|
2022-11-16 20:19:52 +01:00
|
|
|
setHangingProtocol: {
|
|
|
|
|
commandFn: actions.setHangingProtocol,
|
2020-02-14 21:54:08 +01:00
|
|
|
storeContexts: [],
|
2022-07-27 18:39:04 +02:00
|
|
|
options: {},
|
|
|
|
|
},
|
2022-11-16 20:19:52 +01:00
|
|
|
toggleMPR: {
|
|
|
|
|
commandFn: actions.toggleMPR,
|
2022-07-27 18:39:04 +02:00
|
|
|
storeContexts: [],
|
|
|
|
|
options: {},
|
|
|
|
|
},
|
2022-11-16 20:19:52 +01:00
|
|
|
toggleStackImageSync: {
|
|
|
|
|
commandFn: actions.toggleStackImageSync,
|
2020-06-26 20:22:45 +02:00
|
|
|
storeContexts: [],
|
2020-02-20 22:25:26 +01:00
|
|
|
options: {},
|
|
|
|
|
},
|
2022-11-16 20:19:52 +01:00
|
|
|
toggleReferenceLines: {
|
|
|
|
|
commandFn: actions.toggleReferenceLines,
|
2022-09-21 05:40:26 +02:00
|
|
|
storeContexts: [],
|
|
|
|
|
options: {},
|
|
|
|
|
},
|
2019-12-02 20:02:35 +01:00
|
|
|
};
|
2019-06-03 04:43:04 +02:00
|
|
|
|
2019-12-02 20:02:35 +01:00
|
|
|
return {
|
|
|
|
|
actions,
|
|
|
|
|
definitions,
|
2022-07-27 18:39:04 +02:00
|
|
|
defaultContext: 'CORNERSTONE',
|
2019-12-02 20:02:35 +01:00
|
|
|
};
|
2019-06-13 15:50:07 +02:00
|
|
|
};
|
2019-12-02 20:02:35 +01:00
|
|
|
|
|
|
|
|
export default commandsModule;
|