[OHIF-251] - Double click to maximise viewport/ revert to previous layout. (#1860)

* WIP

* double click to maximise implemented at the viewport grid layer.

* Remove memory of cached layout when the layout is changed, or a different displaySet is set.

* WIP fix double click for cornerstone.

* Update CST

* Prevent maximising when double clicking the action bar

* Update extensions/dicom-sr/src/OHIFCornerstoneSRViewport.js

* Apply suggestions from code review

* Fix conflicts.

Co-authored-by: Danny Brown <danny.ri.brown@gmail.com>
This commit is contained in:
James Petts 2020-07-03 18:12:33 +01:00 committed by GitHub
parent 5ee4598bbe
commit 4dfe144bf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 178 additions and 40 deletions

View File

@ -33,7 +33,7 @@
"@ohif/ui": "^0.50.0", "@ohif/ui": "^0.50.0",
"cornerstone-core": "^2.3.0", "cornerstone-core": "^2.3.0",
"cornerstone-math": "^0.1.8", "cornerstone-math": "^0.1.8",
"cornerstone-tools": "4.17.0", "cornerstone-tools": "4.18.0",
"cornerstone-wado-image-loader": "^3.1.2", "cornerstone-wado-image-loader": "^3.1.2",
"dcmjs": "0.14.2", "dcmjs": "0.14.2",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",

View File

@ -38,12 +38,13 @@ export default function init({ servicesManager, configuration }) {
], ],
onSubmit: ({ action, value }) => { onSubmit: ({ action, value }) => {
switch (action.id) { switch (action.id) {
case 'save': callback(value.label); case 'save':
callback(value.label);
} }
UIDialogService.dismiss({ id: dialogId }); UIDialogService.dismiss({ id: dialogId });
}, },
body: ({ value, setValue }) => { body: ({ value, setValue }) => {
const onChangeHandler = (event) => { const onChangeHandler = event => {
event.persist(); event.persist();
setValue(value => ({ ...value, label: event.target.value })); setValue(value => ({ ...value, label: event.target.value }));
}; };
@ -93,7 +94,7 @@ export default function init({ servicesManager, configuration }) {
// THIS // THIS
// is a way for extensions that "depend" on this extension to notify it of // is a way for extensions that "depend" on this extension to notify it of
// new cornerstone enabled elements so it's commands continue to work. // new cornerstone enabled elements so it's commands continue to work.
const handleOhifCornerstoneEnabledElementEvent = function (evt) { const handleOhifCornerstoneEnabledElementEvent = function(evt) {
const { viewportIndex, enabledElement } = evt.detail; const { viewportIndex, enabledElement } = evt.detail;
setEnabledElement(viewportIndex, enabledElement); setEnabledElement(viewportIndex, enabledElement);
@ -271,7 +272,10 @@ const _connectToolsToMeasurementService = measurementService => {
const csToolsVer4MeasurementSource = _initMeasurementService( const csToolsVer4MeasurementSource = _initMeasurementService(
measurementService measurementService
); );
_connectMeasurementServiceToTools(measurementService, csToolsVer4MeasurementSource); _connectMeasurementServiceToTools(
measurementService,
csToolsVer4MeasurementSource
);
const { addOrUpdate, remove } = csToolsVer4MeasurementSource; const { addOrUpdate, remove } = csToolsVer4MeasurementSource;
const elementEnabledEvt = cornerstone.EVENTS.ELEMENT_ENABLED; const elementEnabledEvt = cornerstone.EVENTS.ELEMENT_ENABLED;
@ -336,14 +340,18 @@ const _connectToolsToMeasurementService = measurementService => {
}); });
}; };
const _connectMeasurementServiceToTools = (measurementService, measurementSource) => { const _connectMeasurementServiceToTools = (
const { MEASUREMENTS_CLEARED, MEASUREMENT_REMOVED } = measurementService.EVENTS; measurementService,
measurementSource
) => {
const {
MEASUREMENTS_CLEARED,
MEASUREMENT_REMOVED,
} = measurementService.EVENTS;
const sourceId = measurementSource.id; const sourceId = measurementSource.id;
measurementService.subscribe(MEASUREMENTS_CLEARED, () => { measurementService.subscribe(MEASUREMENTS_CLEARED, () => {
cornerstoneTools.globalImageIdSpecificToolStateManager.restoreToolState( cornerstoneTools.globalImageIdSpecificToolStateManager.restoreToolState({});
{}
);
cornerstone.getEnabledElements().forEach(enabledElement => { cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element); cornerstone.updateImage(enabledElement.element);
}); });

View File

@ -30,7 +30,7 @@
"peerDependencies": { "peerDependencies": {
"@ohif/core": "^0.50.0", "@ohif/core": "^0.50.0",
"cornerstone-core": "^2.2.8", "cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.17.0", "cornerstone-tools": "4.18.0",
"dcmjs": "0.14.2", "dcmjs": "0.14.2",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.8.6", "react": "^16.8.6",

View File

@ -30,7 +30,7 @@
"peerDependencies": { "peerDependencies": {
"@ohif/core": "^0.50.0", "@ohif/core": "^0.50.0",
"cornerstone-core": "^2.2.8", "cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.17.0", "cornerstone-tools": "4.18.0",
"dcmjs": "0.14.2", "dcmjs": "0.14.2",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.8.6", "react": "^16.8.6",

View File

@ -33,7 +33,7 @@
"@ohif/ui": "^0.50.0", "@ohif/ui": "^0.50.0",
"cornerstone-core": "^2.3.0", "cornerstone-core": "^2.3.0",
"cornerstone-math": "^0.1.8", "cornerstone-math": "^0.1.8",
"cornerstone-tools": "4.17.0", "cornerstone-tools": "4.18.0",
"cornerstone-wado-image-loader": "^3.1.2", "cornerstone-wado-image-loader": "^3.1.2",
"dcmjs": "0.14.2", "dcmjs": "0.14.2",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",

View File

@ -399,14 +399,23 @@ function OHIFCornerstoneSRViewport({
}); });
} }
const label =
viewports.length > 1
? _viewportLabels[firstViewportIndexWithMatchingDisplaySetUid]
: '';
return ( return (
<> <>
<ViewportActionBar <ViewportActionBar
onDoubleClick={evt => {
evt.stopPropagation();
evt.preventDefault();
}}
onSeriesChange={onMeasurementChange} onSeriesChange={onMeasurementChange}
onHydrationClick={hydrateMeasurementService} onHydrationClick={hydrateMeasurementService}
showNavArrows={viewportIndex === activeViewportIndex} showNavArrows={viewportIndex === activeViewportIndex}
studyData={{ studyData={{
label: _viewportLabels[firstViewportIndexWithMatchingDisplaySetUid], label,
isTracked: false, isTracked: false,
isLocked: displaySet.isLocked, isLocked: displaySet.isLocked,
isHydrated, isHydrated,
@ -432,18 +441,23 @@ function OHIFCornerstoneSRViewport({
}, },
}} }}
/> />
<CornerstoneViewport <div
onElementEnabled={onElementEnabled} className="relative flex flex-row w-full h-full"
viewportIndex={viewportIndex} onDoubleClick={_onDoubleClick}
imageIds={imageIds} >
imageIdIndex={currentImageIdIndex} <CornerstoneViewport
// TODO: ViewportGrid Context? onElementEnabled={onElementEnabled}
isActive={true} // todo viewportIndex={viewportIndex}
isStackPrefetchEnabled={true} // todo imageIds={imageIds}
isPlaying={false} imageIdIndex={currentImageIdIndex}
frameRate={24} // TODO: ViewportGrid Context?
isOverlayVisible={false} isActive={true} // todo
/> isStackPrefetchEnabled={true} // todo
isPlaying={false}
frameRate={24}
isOverlayVisible={false}
/>
</div>
{childrenWithProps} {childrenWithProps}
</> </>
); );
@ -573,4 +587,14 @@ function _addToolDataToCornerstoneTools(data, toolType, imageId) {
toolData.data.push(data); toolData.data.push(data);
} }
function _onDoubleClick() {
const cancelActiveManipulatorsForElement = cornerstoneTools.getModule(
'manipulatorState'
).setters.cancelActiveManipulatorsForElement;
const enabledElements = cornerstoneTools.store.state.enabledElements;
enabledElements.forEach(element => {
cancelActiveManipulatorsForElement(element);
});
}
export default OHIFCornerstoneSRViewport; export default OHIFCornerstoneSRViewport;

View File

@ -28,7 +28,7 @@
}, },
"peerDependencies": { "peerDependencies": {
"@ohif/core": "^0.50.0", "@ohif/core": "^0.50.0",
"cornerstone-tools": "4.17.0", "cornerstone-tools": "4.18.0",
"dcmjs": "0.14.2", "dcmjs": "0.14.2",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.13.1", "react": "^16.13.1",

View File

@ -295,6 +295,10 @@ function TrackedCornerstoneViewport({
return ( return (
<> <>
<ViewportActionBar <ViewportActionBar
onDoubleClick={evt => {
evt.stopPropagation();
evt.preventDefault();
}}
onSeriesChange={direction => switchMeasurement(direction)} onSeriesChange={direction => switchMeasurement(direction)}
showNavArrows={showNavArrows} showNavArrows={showNavArrows}
studyData={{ studyData={{
@ -324,7 +328,10 @@ function TrackedCornerstoneViewport({
}} }}
/> />
{/* TODO: Viewport interface to accept stack or layers of content like this? */} {/* TODO: Viewport interface to accept stack or layers of content like this? */}
<div className="relative flex flex-row w-full h-full"> <div
className="relative flex flex-row w-full h-full"
onDoubleClick={_onDoubleClick}
>
<CornerstoneViewport <CornerstoneViewport
onElementEnabled={onElementEnabled} onElementEnabled={onElementEnabled}
viewportIndex={viewportIndex} viewportIndex={viewportIndex}
@ -406,6 +413,18 @@ function _getCornerstoneStack(displaySet, dataSource) {
return stack; return stack;
} }
function _onDoubleClick() {
const cancelActiveManipulatorsForElement = cornerstoneTools.getModule(
'manipulatorState'
).setters.cancelActiveManipulatorsForElement;
const enabledElements = cornerstoneTools.store.state.enabledElements;
enabledElements.forEach(element => {
cancelActiveManipulatorsForElement(element);
});
}
/** /**
* Builds the viewport data from a datasource and a displayset. * Builds the viewport data from a datasource and a displayset.
* *
@ -413,6 +432,7 @@ function _getCornerstoneStack(displaySet, dataSource) {
* @param {Object} displaySet * @param {Object} displaySet
* @return {Object} viewport data * @return {Object} viewport data
*/ */
async function _getViewportData(dataSource, displaySet) { async function _getViewportData(dataSource, displaySet) {
const stack = _getCornerstoneStack(displaySet, dataSource); const stack = _getCornerstoneStack(displaySet, dataSource);

View File

@ -57,7 +57,7 @@
"@ohif/core": "^2.9.6", "@ohif/core": "^2.9.6",
"@ohif/ui": "^2.0.0", "@ohif/ui": "^2.0.0",
"cornerstone-core": "^2.3.0", "cornerstone-core": "^2.3.0",
"cornerstone-tools": "4.17.0", "cornerstone-tools": "4.18.0",
"cornerstone-wado-image-loader": "^3.1.2", "cornerstone-wado-image-loader": "^3.1.2",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",
"gh-pages": "^2.0.1", "gh-pages": "^2.0.1",

12
lerna-debug.log Normal file
View File

@ -0,0 +1,12 @@
0 silly argv { _: [ 'run' ],
0 silly argv stream: true,
0 silly argv lernaVersion: '3.22.0',
0 silly argv '$0':
0 silly argv '/Users/jamesapetts/cornerstonejs/Viewers/node_modules/.bin/lerna',
0 silly argv script: 'dev:viewer' }
1 notice cli v3.22.0
2 verbose rootPath /Users/jamesapetts/cornerstonejs/Viewers
3 info versioning independent
4 error JSONError: Unexpected token < in JSON at position 871 while parsing near '...ne-core": "^2.3.0",<<<<<<< HEAD "co...' in platform/core/package.json
4 error at module.exports (/Users/jamesapetts/cornerstonejs/Viewers/node_modules/parse-json/index.js:26:19)
4 error at parse (/Users/jamesapetts/cornerstonejs/Viewers/node_modules/load-json-file/index.js:15:9)

View File

@ -31,7 +31,7 @@
}, },
"peerDependencies": { "peerDependencies": {
"cornerstone-core": "^2.3.0", "cornerstone-core": "^2.3.0",
"cornerstone-tools": "4.17.0", "cornerstone-tools": "4.18.0",
"cornerstone-wado-image-loader": "^3.1.2", "cornerstone-wado-image-loader": "^3.1.2",
"dicom-parser": "^1.8.3" "dicom-parser": "^1.8.3"
}, },

View File

@ -6,14 +6,18 @@ const publicAPI = {
setActiveViewportIndex: _setActiveViewportIndex, setActiveViewportIndex: _setActiveViewportIndex,
setDisplaysetForViewport: _setDisplaysetForViewport, setDisplaysetForViewport: _setDisplaysetForViewport,
setLayout: _setLayout, setLayout: _setLayout,
setCachedLayout: _setCachedLayout,
setServiceImplementation, setServiceImplementation,
}; };
const serviceImplementation = { const serviceImplementation = {
_getState: () => console.warn('getState() NOT IMPLEMENTED'), _getState: () => console.warn('getState() NOT IMPLEMENTED'),
_setActiveViewportIndex: () => console.warn('setActiveViewportIndex() NOT IMPLEMENTED'), _setActiveViewportIndex: () =>
_setDisplaysetForViewport: () => console.warn('setDisplaysetForViewport() NOT IMPLEMENTED'), console.warn('setActiveViewportIndex() NOT IMPLEMENTED'),
_setDisplaysetForViewport: () =>
console.warn('setDisplaysetForViewport() NOT IMPLEMENTED'),
_setLayout: () => console.warn('setLayout() NOT IMPLEMENTED'), _setLayout: () => console.warn('setLayout() NOT IMPLEMENTED'),
_setCachedLayout: () => console.warn('setCachedLayout() NOT IMPLEMENTED'),
}; };
function _getState() { function _getState() {
@ -35,10 +39,15 @@ function _setLayout({ numCols, numRows }) {
return serviceImplementation._setLayout({ numCols, numRows }); return serviceImplementation._setLayout({ numCols, numRows });
} }
function _setCachedLayout({ numCols, numRows, viewports }) {
return serviceImplementation._setLayout({ numCols, numRows, viewports });
}
function setServiceImplementation({ function setServiceImplementation({
getState: getStateImplementation, getState: getStateImplementation,
setActiveViewportIndex: setActiveViewportIndexImplementation, setActiveViewportIndex: setActiveViewportIndexImplementation,
setDisplaysetForViewport: setDisplaysetForViewportImplementation, setDisplaysetForViewport: setDisplaysetForViewportImplementation,
setCachedLayout: setCachedLayoutImplementation,
setLayout: setLayoutImplementation, setLayout: setLayoutImplementation,
}) { }) {
if (getStateImplementation) { if (getStateImplementation) {
@ -53,6 +62,9 @@ function setServiceImplementation({
if (setLayoutImplementation) { if (setLayoutImplementation) {
serviceImplementation._setLayout = setLayoutImplementation; serviceImplementation._setLayout = setLayoutImplementation;
} }
if (setCachedLayoutImplementation) {
serviceImplementation._setCachedLayout = setCachedLayoutImplementation;
}
} }
export default { export default {

View File

@ -16,6 +16,7 @@ const ViewportActionBar = ({
showPatientInfo: patientInfoVisibility, showPatientInfo: patientInfoVisibility,
onSeriesChange, onSeriesChange,
onHydrationClick, onHydrationClick,
onDoubleClick,
}) => { }) => {
const [showPatientInfo, setShowPatientInfo] = useState(patientInfoVisibility); const [showPatientInfo, setShowPatientInfo] = useState(patientInfoVisibility);
@ -135,7 +136,10 @@ const ViewportActionBar = ({
}; };
return ( return (
<div className="flex items-center p-2 border-b select-none border-primary-light min-h-12"> <div
className="flex items-center p-2 border-b select-none border-primary-light min-h-12"
onDoubleClick={onDoubleClick}
>
<div className="flex flex-grow"> <div className="flex flex-grow">
<div className="flex items-center"> <div className="flex items-center">
{renderIconStatus()} {renderIconStatus()}

View File

@ -11,6 +11,7 @@ function ViewportPane({
className, className,
isActive, isActive,
onDrop, onDrop,
onDoubleClick,
onInteraction, onInteraction,
acceptDropsFor, acceptDropsFor,
}) { }) {
@ -57,6 +58,7 @@ function ViewportPane({
// https://reactjs.org/docs/events.html#mouse-events // https://reactjs.org/docs/events.html#mouse-events
// https://stackoverflow.com/questions/8378243/catch-scrolling-event-on-overflowhidden-element // https://stackoverflow.com/questions/8378243/catch-scrolling-event-on-overflowhidden-element
onMouseDown={onInteractionHandler} onMouseDown={onInteractionHandler}
onDoubleClick={onDoubleClick}
onClick={onInteractionHandler} onClick={onInteractionHandler}
onScroll={onInteractionHandler} onScroll={onInteractionHandler}
onWheel={onInteractionHandler} onWheel={onInteractionHandler}
@ -95,6 +97,8 @@ ViewportPane.propTypes = {
onDrop: PropTypes.func.isRequired, onDrop: PropTypes.func.isRequired,
/** Called when the viewportPane is interacted with by the user */ /** Called when the viewportPane is interacted with by the user */
onInteraction: PropTypes.func.isRequired, onInteraction: PropTypes.func.isRequired,
/** Executed when the pane is double clicked */
onDoubleClick: PropTypes.func,
}; };
const noop = () => {}; const noop = () => {};

View File

@ -32,7 +32,7 @@ export function ViewportGridProvider({ children, service }) {
viewports[viewportIndex] = { displaySetInstanceUID, imageIndex }; viewports[viewportIndex] = { displaySetInstanceUID, imageIndex };
return { ...state, ...{ viewports } }; return { ...state, ...{ viewports }, cachedLayout: undefined };
} }
case 'SET_LAYOUT': { case 'SET_LAYOUT': {
const { numCols, numRows } = action.payload; const { numCols, numRows } = action.payload;
@ -51,8 +51,12 @@ export function ViewportGridProvider({ children, service }) {
return { return {
...state, ...state,
...{ activeViewportIndex, numCols, numRows, viewports }, ...{ activeViewportIndex, numCols, numRows, viewports },
cachedLayout: undefined,
}; };
} }
case 'SET_CACHED_LAYOUT': {
return { ...state, cachedLayout: action.payload };
}
default: default:
return action.payload; return action.payload;
} }
@ -93,6 +97,15 @@ export function ViewportGridProvider({ children, service }) {
[dispatch] [dispatch]
); );
const setCachedLayout = useCallback(
payload =>
dispatch({
type: 'SET_CACHED_LAYOUT',
payload,
}),
[dispatch]
);
/** /**
* Sets the implementation of a modal service that can be used by extensions. * Sets the implementation of a modal service that can be used by extensions.
* *
@ -105,6 +118,7 @@ export function ViewportGridProvider({ children, service }) {
setActiveViewportIndex, setActiveViewportIndex,
setDisplaysetForViewport, setDisplaysetForViewport,
setLayout, setLayout,
setCachedLayout,
}); });
} }
}, [ }, [
@ -113,6 +127,7 @@ export function ViewportGridProvider({ children, service }) {
setActiveViewportIndex, setActiveViewportIndex,
setDisplaysetForViewport, setDisplaysetForViewport,
setLayout, setLayout,
setCachedLayout,
]); ]);
const api = { const api = {
@ -120,6 +135,7 @@ export function ViewportGridProvider({ children, service }) {
setActiveViewportIndex, setActiveViewportIndex,
setDisplaysetForViewport, setDisplaysetForViewport,
setLayout, setLayout,
setCachedLayout,
}; };
return ( return (

View File

@ -67,7 +67,7 @@
"classnames": "^2.2.6", "classnames": "^2.2.6",
"core-js": "^3.2.1", "core-js": "^3.2.1",
"cornerstone-math": "^0.1.8", "cornerstone-math": "^0.1.8",
"cornerstone-tools": "4.17.0", "cornerstone-tools": "4.18.0",
"cornerstone-wado-image-loader": "^3.1.2", "cornerstone-wado-image-loader": "^3.1.2",
"dcmjs": "0.14.2", "dcmjs": "0.14.2",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",

View File

@ -11,7 +11,7 @@ const { ImageSet } = classes;
function ViewerViewportGrid(props) { function ViewerViewportGrid(props) {
const { servicesManager, viewportComponents, dataSource } = props; const { servicesManager, viewportComponents, dataSource } = props;
const [ const [
{ numCols, numRows, activeViewportIndex, viewports }, { numCols, numRows, activeViewportIndex, viewports, cachedLayout },
viewportGridService, viewportGridService,
] = useViewportGrid(); ] = useViewportGrid();
@ -67,6 +67,43 @@ function ViewerViewportGrid(props) {
}; };
}; };
const onDoubleClick = viewportIndex => {
if (cachedLayout) {
viewportGridService.setLayout({
numCols: cachedLayout.numCols,
numRows: cachedLayout.numRows,
});
cachedLayout.viewports.forEach((viewport, viewportIndex) => {
viewportGridService.setDisplaysetForViewport({
viewportIndex,
displaySetInstanceUID: viewport.displaySetInstanceUID,
});
});
return;
}
const cachedViewports = viewports.map(viewport => {
return {
displaySetInstanceUID: viewport.displaySetInstanceUID,
};
});
viewportGridService.setDisplaysetForViewport({
viewportIndex: 0,
displaySetInstanceUID: viewports[viewportIndex].displaySetInstanceUID,
});
viewportGridService.setLayout({ numCols: 1, numRows: 1 });
viewportGridService.setCachedLayout({
numCols,
numRows,
viewports: cachedViewports,
});
};
// TODO: // TODO:
// Hmm... Should a "displaySet" being added update the viewport based on HP? // Hmm... Should a "displaySet" being added update the viewport based on HP?
// I guess it might. // I guess it might.
@ -127,6 +164,7 @@ function ViewerViewportGrid(props) {
acceptDropsFor="displayset" acceptDropsFor="displayset"
onDrop={onDropHandler.bind(null, viewportIndex)} onDrop={onDropHandler.bind(null, viewportIndex)}
onInteraction={onInterationHandler} onInteraction={onInterationHandler}
onDoubleClick={() => onDoubleClick(viewportIndex)}
isActive={activeViewportIndex === viewportIndex} isActive={activeViewportIndex === viewportIndex}
> >
<ViewportComponent <ViewportComponent

View File

@ -6766,10 +6766,10 @@ cornerstone-math@^0.1.8:
resolved "https://registry.yarnpkg.com/cornerstone-math/-/cornerstone-math-0.1.8.tgz#68ab1f9e4fdcd7c5cb23a0d2eb4263f9f894f1c5" resolved "https://registry.yarnpkg.com/cornerstone-math/-/cornerstone-math-0.1.8.tgz#68ab1f9e4fdcd7c5cb23a0d2eb4263f9f894f1c5"
integrity sha512-x7NEQHBtVG7j1yeyj/aRoKTpXv1Vh2/H9zNLMyqYJDtJkNng8C4Q8M3CgZ1qer0Yr7eVq2x+Ynmj6kfOm5jXKw== integrity sha512-x7NEQHBtVG7j1yeyj/aRoKTpXv1Vh2/H9zNLMyqYJDtJkNng8C4Q8M3CgZ1qer0Yr7eVq2x+Ynmj6kfOm5jXKw==
cornerstone-tools@4.17.0: cornerstone-tools@4.18.0:
version "4.17.0" version "4.18.0"
resolved "https://registry.yarnpkg.com/cornerstone-tools/-/cornerstone-tools-4.17.0.tgz#a97d6ebeb9745e9f226946e05329e856ebcaee1e" resolved "https://registry.yarnpkg.com/cornerstone-tools/-/cornerstone-tools-4.18.0.tgz#3b9497390fe7a7479db6f480d3219cc20c97e128"
integrity sha512-UPXWPzrb+2XwAXfQLc+ifSb0qp0SBZ3ojlGqt1iq9j85oQuy93avuEN6k2R8BV4NgQFz4okIEJdyKMoBwmrZ8g== integrity sha512-r1IslS/SaybyZq95jhaHltuYuA/c8h6C6iUMYGsuMh1laENYV9Nm0XsI0Ud+w1v5ZgvbHu52bw0rUQmWkOHszA==
dependencies: dependencies:
"@babel/runtime" "7.1.2" "@babel/runtime" "7.1.2"
cornerstone-math "0.1.7" cornerstone-math "0.1.7"