refactor: viewports store (#1239)
* Organize viewports reducers * Organize viewports actions * Add local state to store dom node and remove hack * Comment usage of dom in vtk * Fix set of enabledElements * Fix warning in html viewport * Update docs for state * Add commandsmanager to commandsmodule * refactor: Shift ConnectedViewportDownloadForm to extension-cornerstone (#1224) * refactor: 💡 Shift ConnectedViewportDownloadForm to its own proj Shift ConnectedViewportDownloadForm to its own project and use local state to get enabledElement Closes: #1206 * Refactor connected component to cornerstone component * Remove builtin behaviour from toolbarrow
This commit is contained in:
parent
817d43ee62
commit
d98889f1eb
148
extensions/cornerstone/src/CornerstoneViewportDownloadForm.js
Normal file
148
extensions/cornerstone/src/CornerstoneViewportDownloadForm.js
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import cornerstone from 'cornerstone-core';
|
||||||
|
import cornerstoneTools from 'cornerstone-tools';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import { ViewportDownloadForm } from '@ohif/ui';
|
||||||
|
import { utils } from '@ohif/core';
|
||||||
|
|
||||||
|
import { getEnabledElement } from './state';
|
||||||
|
|
||||||
|
const MINIMUM_SIZE = 100;
|
||||||
|
const DEFAULT_SIZE = 512;
|
||||||
|
const MAX_TEXTURE_SIZE = 10000;
|
||||||
|
|
||||||
|
const CornerstoneViewportDownloadForm = ({ onClose, activeViewportIndex }) => {
|
||||||
|
const activeEnabledElement = getEnabledElement(activeViewportIndex);
|
||||||
|
|
||||||
|
const enableViewport = viewportElement => {
|
||||||
|
if (viewportElement) {
|
||||||
|
cornerstone.enable(viewportElement);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const disableViewport = viewportElement => {
|
||||||
|
if (viewportElement) {
|
||||||
|
cornerstone.disable(viewportElement);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateViewportPreview = (viewportElement, downloadCanvas, fileType) =>
|
||||||
|
new Promise(resolve => {
|
||||||
|
cornerstone.fitToWindow(viewportElement);
|
||||||
|
|
||||||
|
viewportElement.addEventListener(
|
||||||
|
'cornerstoneimagerendered',
|
||||||
|
function updateViewport(event) {
|
||||||
|
const enabledElement = cornerstone.getEnabledElement(event.target)
|
||||||
|
.element;
|
||||||
|
const type = 'image/' + fileType;
|
||||||
|
const dataUrl = downloadCanvas.toDataURL(type, 1);
|
||||||
|
|
||||||
|
let newWidth = enabledElement.offsetHeight;
|
||||||
|
let newHeight = enabledElement.offsetWidth;
|
||||||
|
|
||||||
|
if (newWidth > DEFAULT_SIZE || newHeight > DEFAULT_SIZE) {
|
||||||
|
const multiplier = DEFAULT_SIZE / Math.max(newWidth, newHeight);
|
||||||
|
newHeight *= multiplier;
|
||||||
|
newWidth *= multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve({ dataUrl, width: newWidth, height: newHeight });
|
||||||
|
|
||||||
|
viewportElement.removeEventListener(
|
||||||
|
'cornerstoneimagerendered',
|
||||||
|
updateViewport
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const loadImage = (activeViewport, viewportElement, width, height) =>
|
||||||
|
new Promise(resolve => {
|
||||||
|
if (activeViewport && viewportElement) {
|
||||||
|
const enabledElement = cornerstone.getEnabledElement(activeViewport);
|
||||||
|
const viewport = Object.assign({}, enabledElement.viewport);
|
||||||
|
delete viewport.scale;
|
||||||
|
viewport.translation = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
cornerstone.loadImage(enabledElement.image.imageId).then(image => {
|
||||||
|
cornerstone.displayImage(viewportElement, image);
|
||||||
|
cornerstone.setViewport(viewportElement, viewport);
|
||||||
|
cornerstone.resize(viewportElement, true);
|
||||||
|
|
||||||
|
const newWidth = Math.min(width || image.width, MAX_TEXTURE_SIZE);
|
||||||
|
const newHeight = Math.min(height || image.height, MAX_TEXTURE_SIZE);
|
||||||
|
|
||||||
|
resolve({ image, width: newWidth, height: newHeight });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const toggleAnnotations = (toggle, viewportElement) => {
|
||||||
|
cornerstoneTools.store.state.tools.forEach(({ name }) => {
|
||||||
|
if (toggle) {
|
||||||
|
cornerstoneTools.setToolEnabledForElement(viewportElement, name);
|
||||||
|
} else {
|
||||||
|
cornerstoneTools.setToolDisabledForElement(viewportElement, name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadBlob = (
|
||||||
|
filename,
|
||||||
|
fileType,
|
||||||
|
viewportElement,
|
||||||
|
downloadCanvas
|
||||||
|
) => {
|
||||||
|
const file = `${filename}.${fileType}`;
|
||||||
|
const mimetype = `image/${fileType}`;
|
||||||
|
|
||||||
|
/* Handles JPEG images for IE11 */
|
||||||
|
if (downloadCanvas.msToBlob && fileType === 'jpeg') {
|
||||||
|
const image = downloadCanvas.toDataURL(mimetype, 1);
|
||||||
|
const blob = utils.b64toBlob(
|
||||||
|
image.replace('data:image/jpeg;base64,', ''),
|
||||||
|
mimetype
|
||||||
|
);
|
||||||
|
return window.navigator.msSaveBlob(blob, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
viewportElement.querySelector('canvas').toBlob(blob => {
|
||||||
|
const URLObj = window.URL || window.webkitURL;
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = URLObj.createObjectURL(blob);
|
||||||
|
a.download = file;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ViewportDownloadForm
|
||||||
|
onClose={onClose}
|
||||||
|
minimumSize={MINIMUM_SIZE}
|
||||||
|
maximumSize={MAX_TEXTURE_SIZE}
|
||||||
|
defaultSize={DEFAULT_SIZE}
|
||||||
|
canvasClass={'cornerstone-canvas'}
|
||||||
|
activeViewport={activeEnabledElement}
|
||||||
|
enableViewport={enableViewport}
|
||||||
|
disableViewport={disableViewport}
|
||||||
|
updateViewportPreview={updateViewportPreview}
|
||||||
|
loadImage={loadImage}
|
||||||
|
toggleAnnotations={toggleAnnotations}
|
||||||
|
downloadBlob={downloadBlob}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
CornerstoneViewportDownloadForm.propTypes = {
|
||||||
|
onClose: PropTypes.func,
|
||||||
|
activeViewportIndex: PropTypes.number.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CornerstoneViewportDownloadForm;
|
||||||
@ -1,233 +1,260 @@
|
|||||||
import cornerstone from 'cornerstone-core';
|
import cornerstone from 'cornerstone-core';
|
||||||
import cornerstoneTools from 'cornerstone-tools';
|
import cornerstoneTools from 'cornerstone-tools';
|
||||||
import OHIF from '@ohif/core';
|
import OHIF from '@ohif/core';
|
||||||
|
|
||||||
import { getEnabledElement } from './state';
|
import { getEnabledElement } from './state';
|
||||||
|
import CornerstoneViewportDownloadForm from './CornerstoneViewportDownloadForm';
|
||||||
const scroll = cornerstoneTools.import('util/scroll');
|
const scroll = cornerstoneTools.import('util/scroll');
|
||||||
|
|
||||||
const actions = {
|
const commandsModule = ({ servicesManager }) => {
|
||||||
rotateViewport: ({ viewports, rotation }) => {
|
const actions = {
|
||||||
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
rotateViewport: ({ viewports, rotation }) => {
|
||||||
|
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
||||||
|
|
||||||
if (enabledElement) {
|
if (enabledElement) {
|
||||||
let viewport = cornerstone.getViewport(enabledElement);
|
|
||||||
viewport.rotation += rotation;
|
|
||||||
cornerstone.setViewport(enabledElement, viewport);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
flipViewportHorizontal: ({ viewports }) => {
|
|
||||||
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
|
||||||
|
|
||||||
if (enabledElement) {
|
|
||||||
let viewport = cornerstone.getViewport(enabledElement);
|
|
||||||
viewport.hflip = !viewport.hflip;
|
|
||||||
cornerstone.setViewport(enabledElement, viewport);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
flipViewportVertical: ({ viewports }) => {
|
|
||||||
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
|
||||||
|
|
||||||
if (enabledElement) {
|
|
||||||
let viewport = cornerstone.getViewport(enabledElement);
|
|
||||||
viewport.vflip = !viewport.vflip;
|
|
||||||
cornerstone.setViewport(enabledElement, viewport);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
scaleViewport: ({ direction, viewports }) => {
|
|
||||||
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
|
||||||
const step = direction * 0.15;
|
|
||||||
|
|
||||||
if (enabledElement) {
|
|
||||||
if (step) {
|
|
||||||
let viewport = cornerstone.getViewport(enabledElement);
|
let viewport = cornerstone.getViewport(enabledElement);
|
||||||
viewport.scale += step;
|
viewport.rotation += rotation;
|
||||||
cornerstone.setViewport(enabledElement, viewport);
|
cornerstone.setViewport(enabledElement, viewport);
|
||||||
} else {
|
|
||||||
cornerstone.fitToWindow(enabledElement);
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
flipViewportHorizontal: ({ viewports }) => {
|
||||||
resetViewport: ({ viewports }) => {
|
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
||||||
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
|
||||||
|
|
||||||
if (enabledElement) {
|
if (enabledElement) {
|
||||||
cornerstone.reset(enabledElement);
|
let viewport = cornerstone.getViewport(enabledElement);
|
||||||
}
|
viewport.hflip = !viewport.hflip;
|
||||||
},
|
cornerstone.setViewport(enabledElement, viewport);
|
||||||
invertViewport: ({ viewports }) => {
|
}
|
||||||
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
},
|
||||||
|
flipViewportVertical: ({ viewports }) => {
|
||||||
|
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
||||||
|
|
||||||
if (enabledElement) {
|
if (enabledElement) {
|
||||||
let viewport = cornerstone.getViewport(enabledElement);
|
let viewport = cornerstone.getViewport(enabledElement);
|
||||||
viewport.invert = !viewport.invert;
|
viewport.vflip = !viewport.vflip;
|
||||||
cornerstone.setViewport(enabledElement, viewport);
|
cornerstone.setViewport(enabledElement, viewport);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// TODO: this is receiving `evt` from `ToolbarRow`. We could use it to have
|
scaleViewport: ({ direction, viewports }) => {
|
||||||
// better mouseButtonMask sets.
|
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
||||||
setToolActive: ({ toolName }) => {
|
const step = direction * 0.15;
|
||||||
if (!toolName) {
|
|
||||||
console.warn('No toolname provided to setToolActive command');
|
|
||||||
}
|
|
||||||
cornerstoneTools.setToolActive(toolName, { mouseButtonMask: 1 });
|
|
||||||
},
|
|
||||||
updateViewportDisplaySet: ({ direction }) => {
|
|
||||||
// TODO
|
|
||||||
console.warn('updateDisplaySet: ', direction);
|
|
||||||
},
|
|
||||||
clearAnnotations: ({ viewports }) => {
|
|
||||||
const element = getEnabledElement(viewports.activeViewportIndex);
|
|
||||||
if (!element) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const enabledElement = cornerstone.getEnabledElement(element);
|
if (enabledElement) {
|
||||||
if (!enabledElement || !enabledElement.image) {
|
if (step) {
|
||||||
return;
|
let viewport = cornerstone.getViewport(enabledElement);
|
||||||
}
|
viewport.scale += step;
|
||||||
|
cornerstone.setViewport(enabledElement, viewport);
|
||||||
const {
|
} else {
|
||||||
toolState,
|
cornerstone.fitToWindow(enabledElement);
|
||||||
} = 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;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetViewport: ({ viewports }) => {
|
||||||
|
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
||||||
|
|
||||||
measurementsToRemove.push({
|
if (enabledElement) {
|
||||||
toolType,
|
cornerstone.reset(enabledElement);
|
||||||
_id,
|
}
|
||||||
lesionNamingNumber,
|
},
|
||||||
measurementNumber,
|
invertViewport: ({ viewports }) => {
|
||||||
|
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
||||||
|
|
||||||
|
if (enabledElement) {
|
||||||
|
let viewport = cornerstone.getViewport(enabledElement);
|
||||||
|
viewport.invert = !viewport.invert;
|
||||||
|
cornerstone.setViewport(enabledElement, viewport);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 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');
|
||||||
|
}
|
||||||
|
cornerstoneTools.setToolActive(toolName, { mouseButtonMask: 1 });
|
||||||
|
},
|
||||||
|
updateViewportDisplaySet: ({ direction }) => {
|
||||||
|
// TODO
|
||||||
|
console.warn('updateDisplaySet: ', direction);
|
||||||
|
},
|
||||||
|
clearAnnotations: ({ viewports }) => {
|
||||||
|
const element = getEnabledElement(viewports.activeViewportIndex);
|
||||||
|
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 => {
|
measurementsToRemove.forEach(measurementData => {
|
||||||
OHIF.measurements.MeasurementHandlers.onRemoved({
|
OHIF.measurements.MeasurementHandlers.onRemoved({
|
||||||
detail: {
|
detail: {
|
||||||
toolType: measurementData.toolType,
|
toolType: measurementData.toolType,
|
||||||
measurementData,
|
measurementData,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
nextImage: ({ viewports }) => {
|
||||||
|
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
||||||
|
scroll(enabledElement, 1);
|
||||||
|
},
|
||||||
|
previousImage: ({ viewports }) => {
|
||||||
|
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
||||||
|
scroll(enabledElement, -1);
|
||||||
|
},
|
||||||
|
getActiveViewportEnabledElement: ({ viewports }) => {
|
||||||
|
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
||||||
|
return enabledElement;
|
||||||
|
},
|
||||||
|
showDownloadViewportModal: ({ title, viewports }) => {
|
||||||
|
const activeViewportIndex = viewports.activeViewportIndex;
|
||||||
|
const { UIModalService } = servicesManager.services;
|
||||||
|
UIModalService.show({
|
||||||
|
content: CornerstoneViewportDownloadForm,
|
||||||
|
title,
|
||||||
|
contentProps: {
|
||||||
|
activeViewportIndex,
|
||||||
|
onClose: UIModalService.hide,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
},
|
||||||
},
|
};
|
||||||
nextImage: ({ viewports }) => {
|
|
||||||
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
const definitions = {
|
||||||
scroll(enabledElement, 1);
|
showDownloadViewportModal: {
|
||||||
},
|
commandFn: actions.showDownloadViewportModal,
|
||||||
previousImage: ({ viewports }) => {
|
storeContexts: ['viewports'],
|
||||||
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
options: {},
|
||||||
scroll(enabledElement, -1);
|
},
|
||||||
},
|
getActiveViewportEnabledElement: {
|
||||||
getActiveViewportEnabledElement: ({ viewports }) => {
|
commandFn: actions.getActiveViewportEnabledElement,
|
||||||
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
storeContexts: ['viewports'],
|
||||||
return enabledElement;
|
options: {},
|
||||||
},
|
},
|
||||||
|
rotateViewportCW: {
|
||||||
|
commandFn: actions.rotateViewport,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: { rotation: 90 },
|
||||||
|
},
|
||||||
|
rotateViewportCCW: {
|
||||||
|
commandFn: actions.rotateViewport,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: { rotation: -90 },
|
||||||
|
},
|
||||||
|
invertViewport: {
|
||||||
|
commandFn: actions.invertViewport,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
flipViewportVertical: {
|
||||||
|
commandFn: actions.flipViewportVertical,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
flipViewportHorizontal: {
|
||||||
|
commandFn: actions.flipViewportHorizontal,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
scaleUpViewport: {
|
||||||
|
commandFn: actions.scaleViewport,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: { direction: 1 },
|
||||||
|
},
|
||||||
|
scaleDownViewport: {
|
||||||
|
commandFn: actions.scaleViewport,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: { direction: -1 },
|
||||||
|
},
|
||||||
|
fitViewportToWindow: {
|
||||||
|
commandFn: actions.scaleViewport,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: { direction: 0 },
|
||||||
|
},
|
||||||
|
resetViewport: {
|
||||||
|
commandFn: actions.resetViewport,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
clearAnnotations: {
|
||||||
|
commandFn: actions.clearAnnotations,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
nextImage: {
|
||||||
|
commandFn: actions.nextImage,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
previousImage: {
|
||||||
|
commandFn: actions.previousImage,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
// TODO: First/Last image
|
||||||
|
// Next/Previous series/DisplaySet
|
||||||
|
nextViewportDisplaySet: {
|
||||||
|
commandFn: actions.updateViewportDisplaySet,
|
||||||
|
storeContexts: [],
|
||||||
|
options: { direction: 1 },
|
||||||
|
},
|
||||||
|
previousViewportDisplaySet: {
|
||||||
|
commandFn: actions.updateViewportDisplaySet,
|
||||||
|
storeContexts: [],
|
||||||
|
options: { direction: -1 },
|
||||||
|
},
|
||||||
|
// TOOLS
|
||||||
|
setToolActive: {
|
||||||
|
commandFn: actions.setToolActive,
|
||||||
|
storeContexts: [],
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
actions,
|
||||||
|
definitions,
|
||||||
|
defaultContext: 'ACTIVE_VIEWPORT::CORNERSTONE',
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const definitions = {
|
export default commandsModule;
|
||||||
getActiveViewportEnabledElement: {
|
|
||||||
commandFn: actions.getActiveViewportEnabledElement,
|
|
||||||
storeContexts: ['viewports'],
|
|
||||||
options: {},
|
|
||||||
},
|
|
||||||
rotateViewportCW: {
|
|
||||||
commandFn: actions.rotateViewport,
|
|
||||||
storeContexts: ['viewports'],
|
|
||||||
options: { rotation: 90 },
|
|
||||||
},
|
|
||||||
rotateViewportCCW: {
|
|
||||||
commandFn: actions.rotateViewport,
|
|
||||||
storeContexts: ['viewports'],
|
|
||||||
options: { rotation: -90 },
|
|
||||||
},
|
|
||||||
invertViewport: {
|
|
||||||
commandFn: actions.invertViewport,
|
|
||||||
storeContexts: ['viewports'],
|
|
||||||
options: {},
|
|
||||||
},
|
|
||||||
flipViewportVertical: {
|
|
||||||
commandFn: actions.flipViewportVertical,
|
|
||||||
storeContexts: ['viewports'],
|
|
||||||
options: {},
|
|
||||||
},
|
|
||||||
flipViewportHorizontal: {
|
|
||||||
commandFn: actions.flipViewportHorizontal,
|
|
||||||
storeContexts: ['viewports'],
|
|
||||||
options: {},
|
|
||||||
},
|
|
||||||
scaleUpViewport: {
|
|
||||||
commandFn: actions.scaleViewport,
|
|
||||||
storeContexts: ['viewports'],
|
|
||||||
options: { direction: 1 },
|
|
||||||
},
|
|
||||||
scaleDownViewport: {
|
|
||||||
commandFn: actions.scaleViewport,
|
|
||||||
storeContexts: ['viewports'],
|
|
||||||
options: { direction: -1 },
|
|
||||||
},
|
|
||||||
fitViewportToWindow: {
|
|
||||||
commandFn: actions.scaleViewport,
|
|
||||||
storeContexts: ['viewports'],
|
|
||||||
options: { direction: 0 },
|
|
||||||
},
|
|
||||||
resetViewport: {
|
|
||||||
commandFn: actions.resetViewport,
|
|
||||||
storeContexts: ['viewports'],
|
|
||||||
options: {},
|
|
||||||
},
|
|
||||||
clearAnnotations: {
|
|
||||||
commandFn: actions.clearAnnotations,
|
|
||||||
storeContexts: ['viewports'],
|
|
||||||
options: {},
|
|
||||||
},
|
|
||||||
nextImage: {
|
|
||||||
commandFn: actions.nextImage,
|
|
||||||
storeContexts: ['viewports'],
|
|
||||||
options: {},
|
|
||||||
},
|
|
||||||
previousImage: {
|
|
||||||
commandFn: actions.previousImage,
|
|
||||||
storeContexts: ['viewports'],
|
|
||||||
options: {},
|
|
||||||
},
|
|
||||||
// TODO: First/Last image
|
|
||||||
// Next/Previous series/DisplaySet
|
|
||||||
nextViewportDisplaySet: {
|
|
||||||
commandFn: actions.updateViewportDisplaySet,
|
|
||||||
storeContexts: [],
|
|
||||||
options: { direction: 1 },
|
|
||||||
},
|
|
||||||
previousViewportDisplaySet: {
|
|
||||||
commandFn: actions.updateViewportDisplaySet,
|
|
||||||
storeContexts: [],
|
|
||||||
options: { direction: -1 },
|
|
||||||
},
|
|
||||||
// TOOLS
|
|
||||||
setToolActive: {
|
|
||||||
commandFn: actions.setToolActive,
|
|
||||||
storeContexts: [],
|
|
||||||
options: {},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
|
||||||
actions,
|
|
||||||
definitions,
|
|
||||||
defaultContext: 'ACTIVE_VIEWPORT::CORNERSTONE',
|
|
||||||
};
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import init from './init.js';
|
|||||||
import asyncComponent from './asyncComponent.js';
|
import asyncComponent from './asyncComponent.js';
|
||||||
import commandsModule from './commandsModule.js';
|
import commandsModule from './commandsModule.js';
|
||||||
import toolbarModule from './toolbarModule.js';
|
import toolbarModule from './toolbarModule.js';
|
||||||
|
import CornerstoneViewportDownloadForm from './CornerstoneViewportDownloadForm';
|
||||||
|
|
||||||
const OHIFCornerstoneViewport = asyncComponent(() =>
|
const OHIFCornerstoneViewport = asyncComponent(() =>
|
||||||
import(
|
import(
|
||||||
@ -33,7 +34,9 @@ export default {
|
|||||||
getToolbarModule() {
|
getToolbarModule() {
|
||||||
return toolbarModule;
|
return toolbarModule;
|
||||||
},
|
},
|
||||||
getCommandsModule() {
|
getCommandsModule({ servicesManager }) {
|
||||||
return commandsModule;
|
return commandsModule({ servicesManager });
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export { CornerstoneViewportDownloadForm };
|
||||||
|
|||||||
@ -1,131 +0,0 @@
|
|||||||
import { connect } from 'react-redux';
|
|
||||||
import { ViewportDownloadForm } from '@ohif/ui';
|
|
||||||
import { utils } from '@ohif/core';
|
|
||||||
import cornerstone from 'cornerstone-core';
|
|
||||||
import cornerstoneTools from 'cornerstone-tools';
|
|
||||||
import { commandsManager } from './../App.js';
|
|
||||||
|
|
||||||
const MINIMUM_SIZE = 100;
|
|
||||||
const DEFAULT_SIZE = 512;
|
|
||||||
const MAX_TEXTURE_SIZE = 10000;
|
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => {
|
|
||||||
const activeEnabledElement = commandsManager.runCommand(
|
|
||||||
'getActiveViewportEnabledElement'
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
onClose: ownProps.hide,
|
|
||||||
minimumSize: MINIMUM_SIZE,
|
|
||||||
maximumSize: MAX_TEXTURE_SIZE,
|
|
||||||
defaultSize: DEFAULT_SIZE,
|
|
||||||
canvasClass: 'cornerstone-canvas',
|
|
||||||
activeViewport: activeEnabledElement,
|
|
||||||
enableViewport: viewportElement => {
|
|
||||||
if (viewportElement) {
|
|
||||||
cornerstone.enable(viewportElement);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
disableViewport: viewportElement => {
|
|
||||||
if (viewportElement) {
|
|
||||||
cornerstone.disable(viewportElement);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updateViewportPreview: (viewportElement, downloadCanvas, fileType) =>
|
|
||||||
new Promise(resolve => {
|
|
||||||
cornerstone.fitToWindow(viewportElement);
|
|
||||||
|
|
||||||
viewportElement.addEventListener(
|
|
||||||
'cornerstoneimagerendered',
|
|
||||||
function updateViewport(event) {
|
|
||||||
const enabledElement = cornerstone.getEnabledElement(event.target)
|
|
||||||
.element;
|
|
||||||
const type = 'image/' + fileType;
|
|
||||||
const dataUrl = downloadCanvas.toDataURL(type, 1);
|
|
||||||
|
|
||||||
let newWidth = enabledElement.offsetHeight;
|
|
||||||
let newHeight = enabledElement.offsetWidth;
|
|
||||||
|
|
||||||
if (newWidth > DEFAULT_SIZE || newHeight > DEFAULT_SIZE) {
|
|
||||||
const multiplier = DEFAULT_SIZE / Math.max(newWidth, newHeight);
|
|
||||||
newHeight *= multiplier;
|
|
||||||
newWidth *= multiplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve({ dataUrl, width: newWidth, height: newHeight });
|
|
||||||
|
|
||||||
viewportElement.removeEventListener(
|
|
||||||
'cornerstoneimagerendered',
|
|
||||||
updateViewport
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
loadImage: (activeViewport, viewportElement, width, height) =>
|
|
||||||
new Promise(resolve => {
|
|
||||||
if (activeViewport && viewportElement) {
|
|
||||||
const enabledElement = cornerstone.getEnabledElement(activeViewport);
|
|
||||||
const viewport = Object.assign({}, enabledElement.viewport);
|
|
||||||
delete viewport.scale;
|
|
||||||
viewport.translation = {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
cornerstone.loadImage(enabledElement.image.imageId).then(image => {
|
|
||||||
cornerstone.displayImage(viewportElement, image);
|
|
||||||
cornerstone.setViewport(viewportElement, viewport);
|
|
||||||
cornerstone.resize(viewportElement, true);
|
|
||||||
|
|
||||||
const newWidth = Math.min(width || image.width, MAX_TEXTURE_SIZE);
|
|
||||||
const newHeight = Math.min(
|
|
||||||
height || image.height,
|
|
||||||
MAX_TEXTURE_SIZE
|
|
||||||
);
|
|
||||||
|
|
||||||
resolve({ image, width: newWidth, height: newHeight });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
toggleAnnotations: (toggle, viewportElement) => {
|
|
||||||
cornerstoneTools.store.state.tools.forEach(({ name }) => {
|
|
||||||
if (toggle) {
|
|
||||||
cornerstoneTools.setToolEnabledForElement(viewportElement, name);
|
|
||||||
} else {
|
|
||||||
cornerstoneTools.setToolDisabledForElement(viewportElement, name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
downloadBlob: (filename, fileType, viewportElement, downloadCanvas) => {
|
|
||||||
const file = `${filename}.${fileType}`;
|
|
||||||
const mimetype = `image/${fileType}`;
|
|
||||||
|
|
||||||
/* Handles JPEG images for IE11 */
|
|
||||||
if (downloadCanvas.msToBlob && fileType === 'jpeg') {
|
|
||||||
const image = downloadCanvas.toDataURL(mimetype, 1);
|
|
||||||
const blob = utils.b64toBlob(
|
|
||||||
image.replace('data:image/jpeg;base64,', ''),
|
|
||||||
mimetype
|
|
||||||
);
|
|
||||||
return window.navigator.msSaveBlob(blob, file);
|
|
||||||
}
|
|
||||||
|
|
||||||
viewportElement.querySelector('canvas').toBlob(blob => {
|
|
||||||
const URLObj = window.URL || window.webkitURL;
|
|
||||||
const a = document.createElement('a');
|
|
||||||
a.href = URLObj.createObjectURL(blob);
|
|
||||||
a.download = file;
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
document.body.removeChild(a);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const ConnectedViewportDownloadForm = connect(
|
|
||||||
mapStateToProps,
|
|
||||||
null
|
|
||||||
)(ViewportDownloadForm);
|
|
||||||
|
|
||||||
export default ConnectedViewportDownloadForm;
|
|
||||||
@ -1,7 +1,8 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { MODULE_TYPES } from '@ohif/core';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { withTranslation } from 'react-i18next';
|
import { withTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { MODULE_TYPES } from '@ohif/core';
|
||||||
import {
|
import {
|
||||||
ExpandableToolMenu,
|
ExpandableToolMenu,
|
||||||
RoundedButtonGroup,
|
RoundedButtonGroup,
|
||||||
@ -14,7 +15,6 @@ import './ToolbarRow.css';
|
|||||||
import { commandsManager, extensionManager } from './../App.js';
|
import { commandsManager, extensionManager } from './../App.js';
|
||||||
|
|
||||||
import ConnectedCineDialog from './ConnectedCineDialog';
|
import ConnectedCineDialog from './ConnectedCineDialog';
|
||||||
import ConnectedViewportDownloadForm from './ConnectedViewportDownloadForm';
|
|
||||||
import ConnectedLayoutButton from './ConnectedLayoutButton';
|
import ConnectedLayoutButton from './ConnectedLayoutButton';
|
||||||
import ConnectedPluginSwitch from './ConnectedPluginSwitch.js';
|
import ConnectedPluginSwitch from './ConnectedPluginSwitch.js';
|
||||||
|
|
||||||
@ -308,8 +308,7 @@ function _handleBuiltIn(button) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.behavior === 'DOWNLOAD_SCREEN_SHOT') {
|
if (options.behavior === 'DOWNLOAD_SCREEN_SHOT') {
|
||||||
modal.show({
|
commandsManager.runCommand('showDownloadViewportModal', {
|
||||||
content: ConnectedViewportDownloadForm,
|
|
||||||
title: t('Download High Quality Image'),
|
title: t('Download High Quality Image'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user