feat: Multiple fixes and implementation changes to react-cornerstone-viewport

* Update props for new react-cornerstone-viewport implementation

* Create an preRegistration hook for the cornerstone extension to setup cornerstone tools (takes configuration)

* Isolate measurements panel logic

* reorder extension registration

* remove unused setupTools

* Restore CINE connection

* fix stack prefetcher toggling

* Cleanup OHIFCornerstoneViewport props

* updated yarn lock

* bust yarn.lock to get updated react-cornerstone-viewport

* fix review comment; us isActive to better show it's influence on prefetch

* refactor: remove pass through method

* review performance optimization

* review; comment out unused variable

* shift tool under correct comment

* Use alternative csTools config, if provided

* Note regarding config options
This commit is contained in:
Danny Brown authored and GitHub committed 2019-10-09 10:23:14 -04:00
1 parent 5c5223fcf1
commit 1cc94f36a7
18 files changed
+1584 -1281

No files matched your search

+1 -1
View File
@@ -48,6 +48,6 @@
"@babel/runtime": "^7.5.5",
"classnames": "^2.2.6",
"lodash.throttle": "^4.1.1",
"react-cornerstone-viewport": "0.1.30"
"react-cornerstone-viewport": "2.x.x"
}
}
@@ -23,36 +23,40 @@ const MEASUREMENT_ACTION_MAP = {
const mapStateToProps = (state, ownProps) => {
let dataFromStore;
// TODO: This may not be updated anymore :thinking:
if (state.extensions && state.extensions.cornerstone) {
dataFromStore = state.extensions.cornerstone;
}
// TODO: This should be extension configuration
// ...dataFromStore -->
// availableTools,
// onNewImage,
// onRightClick,
// onTouchPress,
// onTouchStart,
// onMouseClick,
// If this is the active viewport, enable prefetching.
const { viewportIndex } = ownProps; //.viewportData;
const isActive = viewportIndex === state.viewports.activeViewportIndex;
const viewportSpecificData =
state.viewports.viewportSpecificData[viewportIndex] || {};
// CINE
let isPlaying = false;
let frameRate = 24;
if (viewportSpecificData && viewportSpecificData.cine) {
const cine = viewportSpecificData.cine;
isPlaying = cine.isPlaying === true;
frameRate = cine.cineFrameRate || frameRate;
}
return {
layout: state.viewports.layout,
// layout: state.viewports.layout,
isActive,
// TODO: Need a cleaner and more versatile way.
// Currently justing using escape hatch + commands
// activeTool: activeButton && activeButton.command,
...dataFromStore,
enableStackPrefetch: isActive,
isStackPrefetchEnabled: isActive,
isPlaying,
frameRate,
//stack: viewportSpecificData.stack,
cineToolData: viewportSpecificData.cine,
viewport: viewportSpecificData.viewport,
// viewport: viewportSpecificData.viewport,
};
};
@@ -1,10 +1,10 @@
import React, { Component } from "react";
import React, { Component } from 'react';
import ConnectedCornerstoneViewport from "./ConnectedCornerstoneViewport";
import OHIF from "@ohif/core";
import PropTypes from "prop-types";
import cornerstone from "cornerstone-core";
import handleSegmentationStorage from "./handleSegmentationStorage.js";
import ConnectedCornerstoneViewport from './ConnectedCornerstoneViewport';
import OHIF from '@ohif/core';
import PropTypes from 'prop-types';
import cornerstone from 'cornerstone-core';
import handleSegmentationStorage from './handleSegmentationStorage.js';
const { StackManager } = OHIF.utils;
@@ -18,7 +18,7 @@ cornerstone.metaData.addProvider(
StackManager.setMetadataProvider(metadataProvider);
const SOP_CLASSES = {
SEGMENTATION_STORAGE: "1.2.840.10008.5.1.4.1.1.66.4"
SEGMENTATION_STORAGE: '1.2.840.10008.5.1.4.1.1.66.4',
};
const specialCaseHandlers = {};
@@ -28,11 +28,11 @@ specialCaseHandlers[
class OHIFCornerstoneViewport extends Component {
state = {
viewportData: null
viewportData: null,
};
static defaultProps = {
customProps: {}
customProps: {},
};
static propTypes = {
@@ -40,17 +40,17 @@ class OHIFCornerstoneViewport extends Component {
displaySet: PropTypes.object,
viewportIndex: PropTypes.number,
children: PropTypes.node,
customProps: PropTypes.object
customProps: PropTypes.object,
};
static id = "OHIFCornerstoneViewport";
static id = 'OHIFCornerstoneViewport';
static init() {
console.log("OHIFCornerstoneViewport init()");
console.log('OHIFCornerstoneViewport init()');
}
static destroy() {
console.log("OHIFCornerstoneViewport destroy()");
console.log('OHIFCornerstoneViewport destroy()');
StackManager.clearStacks();
}
@@ -72,15 +72,15 @@ class OHIFCornerstoneViewport extends Component {
frameIndex = 0
) {
if (!studies || !studies.length) {
throw new Error("Studies not provided.");
throw new Error('Studies not provided.');
}
if (!studyInstanceUid) {
throw new Error("StudyInstanceUID not provided.");
throw new Error('StudyInstanceUID not provided.');
}
if (!displaySetInstanceUid) {
throw new Error("StudyInstanceUID not provided.");
throw new Error('StudyInstanceUID not provided.');
}
// Create shortcut to displaySet
@@ -89,7 +89,7 @@ class OHIFCornerstoneViewport extends Component {
);
if (!study) {
throw new Error("Study not found.");
throw new Error('Study not found.');
}
const displaySet = study.displaySets.find(set => {
@@ -97,7 +97,7 @@ class OHIFCornerstoneViewport extends Component {
});
if (!displaySet) {
throw new Error("Display Set not found.");
throw new Error('Display Set not found.');
}
// Get stack from Stack Manager
@@ -110,7 +110,7 @@ class OHIFCornerstoneViewport extends Component {
if (sopInstanceUid) {
const index = stack.imageIds.findIndex(imageId => {
const sopCommonModule = cornerstone.metaData.get(
"sopCommonModule",
'sopCommonModule',
imageId
);
if (!sopCommonModule) {
@@ -124,7 +124,7 @@ class OHIFCornerstoneViewport extends Component {
stack.currentImageIdIndex = index;
} else {
console.warn(
"SOPInstanceUID provided was not found in specified DisplaySet"
'SOPInstanceUID provided was not found in specified DisplaySet'
);
}
}
@@ -132,22 +132,6 @@ class OHIFCornerstoneViewport extends Component {
return stack;
}
static getViewportData = (
studies,
studyInstanceUid,
displaySetInstanceUid,
sopInstanceUid,
frameIndex
) => {
return OHIFCornerstoneViewport.getCornerstoneStack(
studies,
studyInstanceUid,
displaySetInstanceUid,
sopInstanceUid,
frameIndex
);
};
getViewportData = async (
studies,
studyInstanceUid,
@@ -172,7 +156,7 @@ class OHIFCornerstoneViewport extends Component {
);
break;
default:
const stack = OHIFCornerstoneViewport.getViewportData(
const stack = OHIFCornerstoneViewport.getCornerstoneStack(
studies,
studyInstanceUid,
displaySetInstanceUid,
@@ -183,7 +167,7 @@ class OHIFCornerstoneViewport extends Component {
viewportData = {
studyInstanceUid,
displaySetInstanceUid,
stack
stack,
};
break;
@@ -199,7 +183,7 @@ class OHIFCornerstoneViewport extends Component {
displaySetInstanceUid,
sopClassUids,
sopInstanceUid,
frameIndex
frameIndex,
} = displaySet;
if (!studyInstanceUid || !displaySetInstanceUid) {
@@ -208,7 +192,7 @@ class OHIFCornerstoneViewport extends Component {
if (sopClassUids && sopClassUids.length > 1) {
console.warn(
"More than one SOPClassUid in the same series is not yet supported."
'More than one SOPClassUid in the same series is not yet supported.'
);
}
@@ -223,7 +207,7 @@ class OHIFCornerstoneViewport extends Component {
frameIndex
).then(viewportData => {
this.setState({
viewportData
viewportData,
});
});
}
@@ -249,25 +233,42 @@ class OHIFCornerstoneViewport extends Component {
render() {
let childrenWithProps = null;
if (!this.state.viewportData) {
return null;
}
const { viewportIndex } = this.props;
const {
imageIds,
currentImageIdIndex,
// If this comes from the instance, would be a better default
// `FrameTime` in the instance
// frameRate = 0,
} = this.state.viewportData.stack;
// TODO: Does it make more sense to use Context?
if (this.props.children && this.props.children.length) {
childrenWithProps = this.props.children.map((child, index) => {
return React.cloneElement(child, {
viewportIndex: this.props.viewportIndex,
key: index
key: index,
});
});
}
return (
<>
{this.state.viewportData && (
<ConnectedCornerstoneViewport
viewportData={this.state.viewportData}
viewportIndex={this.props.viewportIndex}
{...this.props.customProps}
/>
)}
<ConnectedCornerstoneViewport
viewportIndex={viewportIndex}
imageIds={imageIds}
imageIdIndex={currentImageIdIndex}
// ~~ Connected (From REDUX)
// frameRate={frameRate}
// isPlaying={false}
// isStackPrefetchEnabled={true}
// onElementEnabled={() => {}}
// setViewportActive{() => {}}
{...this.props.customProps}
/>
{childrenWithProps}
</>
);
+15 -1
View File
@@ -1,8 +1,13 @@
import init from './init.js';
import asyncComponent from './asyncComponent.js';
import commandsModule from './commandsModule.js';
import toolbarModule from './toolbarModule.js';
const OHIFCornerstoneViewport = asyncComponent(() => import( /* webpackChunkName: "OHIFCornerstoneViewport" */ './OHIFCornerstoneViewport.js'));
const OHIFCornerstoneViewport = asyncComponent(() =>
import(
/* webpackChunkName: "OHIFCornerstoneViewport" */ './OHIFCornerstoneViewport.js'
)
);
/**
*
@@ -13,6 +18,15 @@ export default {
*/
id: 'cornerstone',
/**
*
*
* @param {object} [configuration={}]
* @param {object|array} [configuration.csToolsConfig] - Passed directly to `initCornerstoneTools`
*/
preRegistration(configuration = {}) {
init(configuration);
},
getViewportModule() {
return OHIFCornerstoneViewport;
},
+87
View File
@@ -0,0 +1,87 @@
import OHIF from '@ohif/core';
import cornerstone from 'cornerstone-core';
import csTools from 'cornerstone-tools';
import initCornerstoneTools from './initCornerstoneTools.js';
/**
*
* @param {object} configuration
* @param {Object|Array} configuration.csToolsConfig
*/
export default function init(configuration = {}) {
const { csToolsConfig } = configuration;
const { StackManager } = OHIF.utils;
const metadataProvider = new OHIF.cornerstone.MetadataProvider();
// ~~ Set our MetadataProvider
cornerstone.metaData.addProvider(
metadataProvider.provider.bind(metadataProvider)
);
StackManager.setMetadataProvider(metadataProvider);
// ~~
const defaultCsToolsConfig = csToolsConfig || {
globalToolSyncEnabled: true,
showSVGCursors: true,
autoResizeViewports: false,
};
initCornerstoneTools(defaultCsToolsConfig);
// ~~ Toooools 🙌
const {
PanTool,
ZoomTool,
WwwcTool,
MagnifyTool,
StackScrollTool,
StackScrollMouseWheelTool,
// Touch
PanMultiTouchTool,
ZoomTouchPinchTool,
// Annotations
EraserTool,
ArrowAnnotateTool,
BidirectionalTool,
LengthTool,
AngleTool,
FreehandRoiTool,
EllipticalRoiTool,
DragProbeTool,
RectangleRoiTool,
// Segmentation
BrushTool,
} = csTools;
const tools = [
PanTool,
ZoomTool,
WwwcTool,
MagnifyTool,
StackScrollTool,
StackScrollMouseWheelTool,
// Touch
PanMultiTouchTool,
ZoomTouchPinchTool,
// Annotations
EraserTool,
ArrowAnnotateTool,
BidirectionalTool,
LengthTool,
AngleTool,
FreehandRoiTool,
EllipticalRoiTool,
DragProbeTool,
RectangleRoiTool,
// Segmentation
BrushTool,
];
tools.forEach(tool => csTools.addTool(tool));
csTools.setToolActive('Pan', { mouseButtonMask: 4 });
csTools.setToolActive('Zoom', { mouseButtonMask: 2 });
csTools.setToolActive('Wwwc', { mouseButtonMask: 1 });
csTools.setToolActive('StackScrollMouseWheel', {}); // TODO: Empty options should not be required
csTools.setToolActive('PanMultiTouch', { pointers: 2 }); // TODO: Better error if no options
csTools.setToolActive('ZoomTouchPinch', {});
}
@@ -0,0 +1,27 @@
import Hammer from 'hammerjs';
import cornerstone from 'cornerstone-core';
import cornerstoneMath from 'cornerstone-math';
import cornerstoneTools from 'cornerstone-tools';
export default function(configuration = {}) {
// For debugging
window.cornerstoneTools = cornerstoneTools;
cornerstoneTools.external.cornerstone = cornerstone;
cornerstoneTools.external.Hammer = Hammer;
cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
cornerstoneTools.init(configuration);
// Set the tool font and font size
// context.font = "[style] [variant] [weight] [size]/[line height] [font family]";
const fontFamily =
'Roboto, OpenSans, HelveticaNeue-Light, Helvetica Neue Light, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif';
cornerstoneTools.textStyle.setFont(`16px ${fontFamily}`);
// Tool styles/colors
cornerstoneTools.toolStyle.setToolWidth(2);
cornerstoneTools.toolColors.setToolColor('rgb(255, 255, 0)');
cornerstoneTools.toolColors.setActiveColor('rgb(0, 255, 0)');
cornerstoneTools.store.state.touchProximity = 40;
}