ohif-viewer/extensions/cornerstone/src/commandsModule.js
2021-12-22 11:56:57 -05:00

503 lines
14 KiB
JavaScript

import cornerstone from 'cornerstone-core';
import cornerstoneTools from 'cornerstone-tools';
import OHIF from '@ohif/core';
//import setCornerstoneLayout from './utils/setCornerstoneLayout.js';
import { getEnabledElement } from './state';
import CornerstoneViewportDownloadForm from './CornerstoneViewportDownloadForm';
const scroll = cornerstoneTools.import('util/scroll');
const { studyMetadataManager } = OHIF.utils;
const imagePositionSynchronizer = new cornerstoneTools.Synchronizer(
cornerstone.EVENTS.NEW_IMAGE,
cornerstoneTools.stackImagePositionSynchronizer
);
const panZoomSynchronizer = new cornerstoneTools.Synchronizer(
cornerstone.EVENTS.IMAGE_RENDERED,
cornerstoneTools.panZoomSynchronizer
);
function onElementEnabledAddToSync(event) {
const { element } = event.detail;
imagePositionSynchronizer.add(element);
// panZoomSynchronizer.add(element);
}
function onElementDisabledRemoveFromSync(event) {
const { element } = event.detail;
imagePositionSynchronizer.remove(element);
panZoomSynchronizer.remove(element);
}
const commandsModule = ({ servicesManager, commandsManager }) => {
const { ViewportGridService } = servicesManager.services;
function _getActiveViewportsEnabledElement() {
const { activeViewportIndex } = ViewportGridService.getState();
const { element } = getEnabledElement(activeViewportIndex) || {};
return element;
}
const actions = {
getCornerstoneLibraries: () => {
return { cornerstone, cornerstoneTools };
},
rotateViewport: ({ rotation }) => {
const enabledElement = _getActiveViewportsEnabledElement();
if (enabledElement) {
let viewport = cornerstone.getViewport(enabledElement);
viewport.rotation += rotation;
cornerstone.setViewport(enabledElement, viewport);
}
},
flipViewportHorizontal: () => {
const enabledElement = _getActiveViewportsEnabledElement();
if (enabledElement) {
let viewport = cornerstone.getViewport(enabledElement);
viewport.hflip = !viewport.hflip;
cornerstone.setViewport(enabledElement, viewport);
}
},
flipViewportVertical: () => {
const enabledElement = _getActiveViewportsEnabledElement();
if (enabledElement) {
let viewport = cornerstone.getViewport(enabledElement);
viewport.vflip = !viewport.vflip;
cornerstone.setViewport(enabledElement, viewport);
}
},
scaleViewport: ({ direction }) => {
const enabledElement = _getActiveViewportsEnabledElement();
const step = direction * 0.15;
if (enabledElement) {
if (step) {
let viewport = cornerstone.getViewport(enabledElement);
viewport.scale += step;
cornerstone.setViewport(enabledElement, viewport);
} else {
cornerstone.fitToWindow(enabledElement);
}
}
},
resetViewport: () => {
const enabledElement = _getActiveViewportsEnabledElement();
if (enabledElement) {
cornerstone.reset(enabledElement);
}
},
toggleSynchronizer: ({ toggledState }) => {
const synchronizers = [imagePositionSynchronizer];
// Set synchronizer state when the command is run.
synchronizers.forEach(s => {
s.enabled = toggledState;
});
const unsubscribe = () => {
cornerstone.events.removeEventListener(
cornerstone.EVENTS.ELEMENT_ENABLED,
onElementEnabledAddToSync
);
cornerstone.events.removeEventListener(
cornerstone.EVENTS.ELEMENT_DISABLED,
onElementDisabledRemoveFromSync
);
};
const subscribe = () => {
cornerstone.events.addEventListener(
cornerstone.EVENTS.ELEMENT_ENABLED,
onElementEnabledAddToSync
);
cornerstone.events.addEventListener(
cornerstone.EVENTS.ELEMENT_DISABLED,
onElementDisabledRemoveFromSync
);
};
// Add event handlers so that if the layout is changed, new elements
// are automatically added to the synchronizer while it is enabled.
if (toggledState === true) {
subscribe();
} else {
// If the synchronizer is disabled, remove the event handlers
unsubscribe();
}
// Erase existing state and then set up all currently existing elements
cornerstone.getEnabledElements().map(e => {
synchronizers.forEach(s => {
s.remove(e.element);
s.add(e.element);
});
});
return unsubscribe;
},
invertViewport: ({ element }) => {
let enabledElement;
if (element === undefined) {
enabledElement = _getActiveViewportsEnabledElement();
} else {
enabledElement = element;
}
if (enabledElement) {
let viewport = cornerstone.getViewport(enabledElement);
viewport.invert = !viewport.invert;
cornerstone.setViewport(enabledElement, viewport);
}
},
cancelMeasurement: () => {
const enabledElement = _getActiveViewportsEnabledElement();
if (enabledElement) {
const cancelActiveManipulatorsForElement = cornerstoneTools.getModule(
'manipulatorState'
).setters.cancelActiveManipulatorsForElement;
cancelActiveManipulatorsForElement(enabledElement);
cornerstone.updateImage(enabledElement);
}
},
// TODO: this is receiving `evt` from `ToolbarRow`. We could use it to have
// better mouseButtonMask sets.
setToolActive: ({ toolName }) => {
if (!toolName) {
console.warn('No toolname provided to setToolActive command');
}
// Find total number of tool indexes
const { viewports } = ViewportGridService.getState() || { viewports: [] };
for (let i = 0; i < viewports.length; i++) {
const viewport = viewports[i];
const hasDisplaySet = viewport.displaySetInstanceUID !== undefined;
if (!hasDisplaySet) {
continue;
}
const viewportInfo = getEnabledElement(i);
const hasCornerstoneContext =
viewportInfo.context === 'ACTIVE_VIEWPORT::CORNERSTONE';
if (hasCornerstoneContext) {
cornerstoneTools.setToolActiveForElement(
viewportInfo.element,
toolName,
{ mouseButtonMask: 1 }
);
} else {
commandsManager.runCommand(
'setToolActive',
{
element: viewportInfo.element,
toolName,
},
viewportInfo.context
);
}
}
},
clearAnnotations: () => {
const element = _getActiveViewportsEnabledElement();
if (!element) {
return;
}
const { enabledElement } = cornerstone.getEnabledElement(element) || {};
if (!enabledElement || !enabledElement.image) {
return;
}
const {
toolState,
} = cornerstoneTools.globalImageIdSpecificToolStateManager;
if (
!toolState ||
toolState.hasOwnProperty(enabledElement.image.imageId) === false
) {
return;
}
const imageIdToolState = toolState[enabledElement.image.imageId];
const measurementsToRemove = [];
Object.keys(imageIdToolState).forEach(toolType => {
const { data } = imageIdToolState[toolType];
data.forEach(measurementData => {
const {
_id,
lesionNamingNumber,
measurementNumber,
} = measurementData;
if (!_id) {
return;
}
measurementsToRemove.push({
toolType,
_id,
lesionNamingNumber,
measurementNumber,
});
});
});
measurementsToRemove.forEach(measurementData => {
OHIF.measurements.MeasurementHandlers.onRemoved({
detail: {
toolType: measurementData.toolType,
measurementData,
},
});
});
},
nextImage: () => {
const enabledElement = _getActiveViewportsEnabledElement();
scroll(enabledElement, 1);
},
previousImage: () => {
const enabledElement = _getActiveViewportsEnabledElement();
scroll(enabledElement, -1);
},
getActiveViewportEnabledElement: () => {
const enabledElement = _getActiveViewportsEnabledElement();
return enabledElement;
},
showDownloadViewportModal: () => {
const { activeViewportIndex } = ViewportGridService.getState();
const { UIModalService } = servicesManager.services;
if (UIModalService) {
UIModalService.show({
content: CornerstoneViewportDownloadForm,
title: 'Download High Quality Image',
contentProps: {
activeViewportIndex,
onClose: UIModalService.hide,
},
});
}
},
getNearbyToolData({ element, canvasCoordinates, availableToolTypes }) {
const nearbyTool = {};
let pointNearTool = false;
availableToolTypes.forEach(toolType => {
const elementToolData = cornerstoneTools.getToolState(
element,
toolType
);
if (!elementToolData) {
return;
}
elementToolData.data.forEach((toolData, index) => {
let elementToolInstance = cornerstoneTools.getToolForElement(
element,
toolType
);
if (!elementToolInstance) {
elementToolInstance = cornerstoneTools.getToolForElement(
element,
`${toolType}Tool`
);
}
if (!elementToolInstance) {
console.warn('Tool not found.');
return undefined;
}
if (
elementToolInstance.pointNearTool(
element,
toolData,
canvasCoordinates
)
) {
pointNearTool = true;
nearbyTool.tool = toolData;
nearbyTool.index = index;
nearbyTool.toolType = toolType;
}
});
if (pointNearTool) {
return false;
}
});
return pointNearTool ? nearbyTool : undefined;
},
removeToolState: ({ element, toolType, tool }) => {
cornerstoneTools.removeToolState(element, toolType, tool);
cornerstone.updateImage(element);
},
// setCornerstoneLayout: () => {
// setCornerstoneLayout();
// },
setWindowLevel: ({ window, level }) => {
const enabledElement = _getActiveViewportsEnabledElement();
if (enabledElement) {
let viewport = cornerstone.getViewport(enabledElement);
viewport.voi = {
windowWidth: Number(window),
windowCenter: Number(level),
};
cornerstone.setViewport(enabledElement, viewport);
}
},
};
const definitions = {
jumpToImage: {
commandFn: actions.jumpToImage,
storeContexts: [],
options: {},
},
getCornerstoneLibraries: {
commandFn: actions.getCornerstoneLibraries,
storeContexts: [],
options: {},
context: 'VIEWER',
},
getNearbyToolData: {
commandFn: actions.getNearbyToolData,
storeContexts: [],
options: {},
},
toggleSynchronizer: {
commandFn: actions.toggleSynchronizer,
storeContexts: [],
options: {},
},
removeToolState: {
commandFn: actions.removeToolState,
storeContexts: [],
options: {},
},
showDownloadViewportModal: {
commandFn: actions.showDownloadViewportModal,
storeContexts: [],
options: {},
},
getActiveViewportEnabledElement: {
commandFn: actions.getActiveViewportEnabledElement,
storeContexts: [],
options: {},
},
rotateViewportCW: {
commandFn: actions.rotateViewport,
storeContexts: [],
options: { rotation: 90 },
},
rotateViewportCCW: {
commandFn: actions.rotateViewport,
storeContexts: [],
options: { rotation: -90 },
},
invertViewport: {
commandFn: actions.invertViewport,
storeContexts: [],
options: {},
},
cancelMeasurement: {
commandFn: actions.cancelMeasurement,
storeContexts: [],
options: {},
},
flipViewportVertical: {
commandFn: actions.flipViewportVertical,
storeContexts: [],
options: {},
},
flipViewportHorizontal: {
commandFn: actions.flipViewportHorizontal,
storeContexts: [],
options: {},
},
scaleUpViewport: {
commandFn: actions.scaleViewport,
storeContexts: [],
options: { direction: 1 },
},
scaleDownViewport: {
commandFn: actions.scaleViewport,
storeContexts: [],
options: { direction: -1 },
},
fitViewportToWindow: {
commandFn: actions.scaleViewport,
storeContexts: [],
options: { direction: 0 },
},
resetViewport: {
commandFn: actions.resetViewport,
storeContexts: [],
options: {},
},
clearAnnotations: {
commandFn: actions.clearAnnotations,
storeContexts: [],
options: {},
},
nextImage: {
commandFn: actions.nextImage,
storeContexts: [],
options: {},
},
previousImage: {
commandFn: actions.previousImage,
storeContexts: [],
options: {},
},
// TOOLS
setToolActive: {
commandFn: actions.setToolActive,
storeContexts: [],
options: {},
},
setZoomTool: {
commandFn: actions.setToolActive,
storeContexts: [],
options: { toolName: 'Zoom' },
},
// setCornerstoneLayout: {
// commandFn: actions.setCornerstoneLayout,
// storeContexts: [],
// options: {},
// context: 'VIEWER',
// },
setWindowLevel: {
commandFn: actions.setWindowLevel,
storeContexts: [],
options: {},
},
};
return {
actions,
definitions,
defaultContext: 'ACTIVE_VIEWPORT::CORNERSTONE',
};
};
export default commandsModule;