Merge pull request #556 from OHIF/vtk-extension-changes
refactor(@ohif/extension-vtk): Update VTK extension to add basic MPR …
This commit is contained in:
commit
d0d757d468
@ -1,71 +1,81 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-vtk",
|
"name": "@ohif/extension-vtk",
|
||||||
"version": "0.0.3",
|
"version": "0.0.4",
|
||||||
"description": "OHIF extension for VTK.js",
|
"description": "OHIF extension for VTK.js",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": "OHIF/Viewers",
|
"repository": "OHIF/Viewers",
|
||||||
"main": "dist/index.umd.js",
|
"main": "dist/index.umd.js",
|
||||||
"module": "dist/index.es.js",
|
|
||||||
"jsnext:main": "dist/index.es.js",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8",
|
"node": ">=8",
|
||||||
"npm": ">=5"
|
"npm": ">=5"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rollup -c",
|
"build": "webpack --progress --colors --mode development",
|
||||||
"prepublishOnly": "npm run build",
|
"build:release": "webpack --progress --colors --mode production",
|
||||||
"start": "rollup -c -w",
|
"start": "webpack --watch --progress --colors --mode development",
|
||||||
|
"prepare": "yarn run build:release",
|
||||||
|
"predeploy": "cd example && yarn install && yarn run build:release",
|
||||||
|
"prepublishOnly": "yarn run build:release",
|
||||||
"lint": "eslint -c .eslintrc --fix src && prettier --single-quote --write src/**/*.{js,jsx,json,css}"
|
"lint": "eslint -c .eslintrc --fix src && prettier --single-quote --write src/**/*.{js,jsx,json,css}"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
"@ohif/i18n": "0.0.4",
|
||||||
|
"i18next": "^17.0.3",
|
||||||
|
"i18next-browser-languagedetector": "^3.0.1",
|
||||||
|
"react-i18next": "^10.11.0",
|
||||||
"cornerstone-core": "^2.2.8",
|
"cornerstone-core": "^2.2.8",
|
||||||
"cornerstone-wado-image-loader": "^2.2.3",
|
"cornerstone-wado-image-loader": "^2.2.3",
|
||||||
"dcmjs": "^0.3.6",
|
"dcmjs": "^0.4.7",
|
||||||
"dicom-parser": "^1.8.3",
|
"dicom-parser": "^1.8.3",
|
||||||
"hammerjs": "^2.0.8",
|
"ohif-core": "^0.5.9",
|
||||||
"ohif-core": "^0.3.3",
|
"prop-types": "^15.7.2",
|
||||||
"@ohif/i18n": "0.0.4",
|
"react": "^16.8.6",
|
||||||
"prop-types": "^15.6.2",
|
"react-dom": "^16.8.6",
|
||||||
"react": "^15.0.0 || ^16.0.0",
|
"react-redux": "^7.1.0",
|
||||||
"react-dom": "^15.0.0 || ^16.0.0",
|
"react-resize-detector": "^4.2.0",
|
||||||
"react-redux": "^6.0.0",
|
"react-viewerbase": "^0.8.1",
|
||||||
"react-resize-detector": "^3.4.0",
|
|
||||||
"react-viewerbase": "^0.6.0",
|
|
||||||
"redux": "^4.0.1"
|
"redux": "^4.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.2.2",
|
"@babel/core": "^7.4.5",
|
||||||
"@babel/plugin-external-helpers": "^7.2.0",
|
"@babel/plugin-external-helpers": "^7.2.0",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.2.3",
|
"@babel/plugin-proposal-class-properties": "^7.4.4",
|
||||||
"@babel/plugin-transform-runtime": "^7.2.0",
|
"@babel/plugin-transform-runtime": "^7.4.4",
|
||||||
"@babel/preset-env": "^7.2.3",
|
"@babel/preset-env": "^7.4.5",
|
||||||
"@babel/preset-react": "^7.0.0",
|
"@babel/preset-react": "^7.0.0",
|
||||||
|
"i18next": "^17.0.3",
|
||||||
|
"i18next-browser-languagedetector": "^3.0.1",
|
||||||
|
"react-i18next": "^10.11.0",
|
||||||
"babel-eslint": "^10.0.1",
|
"babel-eslint": "^10.0.1",
|
||||||
|
"babel-loader": "^8.0.6",
|
||||||
|
"cornerstone-tools": "^3.13.0",
|
||||||
|
"cornerstone-wado-image-loader": "^2.2.3",
|
||||||
"cross-env": "^5.2.0",
|
"cross-env": "^5.2.0",
|
||||||
"eslint": "5.13.0",
|
"dcmjs": "^0.4.7",
|
||||||
"eslint-plugin-import": "^2.14.0",
|
"dicom-parser": "^1.8.3",
|
||||||
"eslint-plugin-node": "^8.0.0",
|
"eslint": "5.16.0",
|
||||||
"eslint-plugin-promise": "^4.0.1",
|
"eslint-plugin-import": "^2.17.3",
|
||||||
"eslint-plugin-react": "^7.11.1",
|
"eslint-plugin-node": "^9.1.0",
|
||||||
|
"eslint-plugin-promise": "^4.1.1",
|
||||||
|
"eslint-plugin-react": "^7.13.0",
|
||||||
"gh-pages": "^2.0.1",
|
"gh-pages": "^2.0.1",
|
||||||
"husky": "^1.3.1",
|
"husky": "^2.4.1",
|
||||||
"lint-staged": "^8.1.0",
|
"lint-staged": "^8.2.0",
|
||||||
"prettier": "^1.15.3",
|
"ohif-core": "^0.5.9",
|
||||||
|
"prettier": "^1.18.2",
|
||||||
"react": "^16.6.3",
|
"react": "^16.6.3",
|
||||||
"react-dom": "^16.6.3",
|
"react-dom": "^16.6.3",
|
||||||
"rollup": "^1.1.2",
|
"react-redux": "^7.1.0",
|
||||||
"rollup-plugin-babel": "^4.2.0",
|
"react-viewerbase": "^0.8.1",
|
||||||
"rollup-plugin-commonjs": "^9.2.0",
|
"redux": "^4.0.1",
|
||||||
"rollup-plugin-copy": "^2.0.1",
|
"shader-loader": "^1.3.1",
|
||||||
"rollup-plugin-node-builtins": "^2.1.2",
|
"stylelint": "^10.1.0",
|
||||||
"rollup-plugin-node-resolve": "^4.0.0",
|
"stylelint-config-recommended": "^2.2.0",
|
||||||
"rollup-plugin-peer-deps-external": "^2.2.0",
|
"stylus": "^0.54.5",
|
||||||
"rollup-plugin-postcss": "^2.0.3",
|
"webpack": "^4.33.0",
|
||||||
"rollup-plugin-url": "^2.1.0",
|
"webpack-cli": "^3.3.4",
|
||||||
"stylelint": "^9.9.0",
|
"worker-loader": "^2.0.0"
|
||||||
"stylelint-config-recommended": "^2.1.0",
|
|
||||||
"stylus": "^0.54.5"
|
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
"hooks": {
|
"hooks": {
|
||||||
@ -92,8 +102,8 @@
|
|||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.2.0",
|
"@babel/runtime": "^7.4.5",
|
||||||
"classnames": "^2.2.6",
|
"@ohif/i18n": "0.0.4",
|
||||||
"react-vtkjs-viewport": "^0.0.7"
|
"react-vtkjs-viewport": "0.0.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,33 +5,50 @@ import OHIF from 'ohif-core';
|
|||||||
const { setToolActive } = OHIF.redux.actions;
|
const { setToolActive } = OHIF.redux.actions;
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
const activeButton = state.tools.buttons.find(tool => tool.active === true);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
buttons: [
|
buttons: [
|
||||||
|
{
|
||||||
|
command: 'Crosshairs',
|
||||||
|
type: 'tool',
|
||||||
|
text: 'Crosshairs',
|
||||||
|
icon: 'crosshairs',
|
||||||
|
active: true,
|
||||||
|
onClick: () => {
|
||||||
|
// TODO: Make these use setToolActive instead
|
||||||
|
window.commandsManager.runCommand('enableCrosshairsTool', {}, 'vtk');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: 'WWWC',
|
||||||
|
type: 'tool',
|
||||||
|
text: 'WWWC',
|
||||||
|
icon: 'level',
|
||||||
|
active: true,
|
||||||
|
onClick: () => {
|
||||||
|
// TODO: Make these use setToolActive instead
|
||||||
|
window.commandsManager.runCommand('enableLevelTool', {}, 'vtk');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Disabled temporarily due to issues with the interactor style
|
||||||
{
|
{
|
||||||
command: 'Rotate',
|
command: 'Rotate',
|
||||||
type: 'tool',
|
type: 'tool',
|
||||||
text: 'Rotate',
|
text: 'Rotate',
|
||||||
icon: '3d-rotate',
|
icon: '3d-rotate',
|
||||||
active: true
|
active: false,
|
||||||
}
|
onClick: () => {
|
||||||
|
// TODO: Make these use setToolActive instead
|
||||||
|
window.commandsManager.runCommand('enableRotateTool', {}, 'vtk');
|
||||||
|
}
|
||||||
|
},*/
|
||||||
],
|
],
|
||||||
activeCommand: 'Rotate'
|
activeCommand: 'Crosshairs'
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => {
|
|
||||||
return {
|
|
||||||
setToolActive: tool => {
|
|
||||||
//dispatch(setToolActive(tool.command))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const ConnectedToolbarSection = connect(
|
const ConnectedToolbarSection = connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
mapDispatchToProps
|
null
|
||||||
)(ToolbarSection);
|
)(ToolbarSection);
|
||||||
|
|
||||||
export default ConnectedToolbarSection;
|
export default ConnectedToolbarSection;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { VTKMPRViewport } from 'react-vtkjs-viewport';
|
import { View2D } from 'react-vtkjs-viewport';
|
||||||
import OHIF from 'ohif-core';
|
import OHIF from 'ohif-core';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -17,7 +17,7 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If this is the active viewport, enable prefetching.
|
// If this is the active viewport, enable prefetching.
|
||||||
const { viewportIndex } = ownProps; //.viewportData;
|
const { viewportIndex } = ownProps;
|
||||||
const isActive = viewportIndex === state.viewports.activeViewportIndex;
|
const isActive = viewportIndex === state.viewports.activeViewportIndex;
|
||||||
const viewportSpecificData =
|
const viewportSpecificData =
|
||||||
state.viewports.viewportSpecificData[viewportIndex] || {};
|
state.viewports.viewportSpecificData[viewportIndex] || {};
|
||||||
@ -31,7 +31,7 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
...pluginDetails,
|
...pluginDetails,
|
||||||
activeTool: activeButton && activeButton.command,
|
activeTool: activeButton && activeButton.command,
|
||||||
...dataFromStore,
|
...dataFromStore,
|
||||||
enableStackPrefetch: isActive,
|
enableStackPrefetch: isActive
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -45,17 +45,44 @@ const mapDispatchToProps = (dispatch, ownProps) => {
|
|||||||
|
|
||||||
setViewportSpecificData: data => {
|
setViewportSpecificData: data => {
|
||||||
dispatch(setViewportSpecificData(viewportIndex, data));
|
dispatch(setViewportSpecificData(viewportIndex, data));
|
||||||
},
|
|
||||||
|
|
||||||
clearViewportSpecificData: () => {
|
|
||||||
dispatch(clearViewportSpecificData(viewportIndex));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mergeProps = (propsFromState, propsFromDispatch, ownProps) => {
|
||||||
|
const { afterCreation } = propsFromState;
|
||||||
|
const { setViewportSpecificData } = propsFromDispatch;
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
...propsFromState,
|
||||||
|
...propsFromDispatch,
|
||||||
|
...ownProps,
|
||||||
|
/**
|
||||||
|
* Our component sets up the underlying dom element on "componentDidMount"
|
||||||
|
* for use with VTK.
|
||||||
|
*
|
||||||
|
* The onCreated prop passes back an Object containing many of the internal
|
||||||
|
* components of the VTK scene. We can grab a reference to these here, to
|
||||||
|
* make playing with VTK's native methods easier.
|
||||||
|
*
|
||||||
|
* A similar approach is taken with the Cornerstone extension.
|
||||||
|
*/
|
||||||
|
onCreated: api => {
|
||||||
|
// Store the API details for later
|
||||||
|
//setViewportSpecificData({ vtkApi: api });
|
||||||
|
|
||||||
|
if (afterCreation && typeof afterCreation === 'function') {
|
||||||
|
afterCreation(api);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return props;
|
||||||
|
};
|
||||||
|
|
||||||
const ConnectedVTKViewport = connect(
|
const ConnectedVTKViewport = connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
//mapDispatchToProps
|
mapDispatchToProps,
|
||||||
)(VTKMPRViewport);
|
mergeProps
|
||||||
|
)(View2D);
|
||||||
|
|
||||||
export default ConnectedVTKViewport;
|
export default ConnectedVTKViewport;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import OHIFVTKViewport from './OHIFVTKViewport.js';
|
import OHIFVTKViewport from './OHIFVTKViewport.js';
|
||||||
import ToolbarModule from './ToolbarModule.js';
|
import ToolbarModule from './ToolbarModule.js';
|
||||||
|
import { definitions } from './commands';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pass in 'children' to a React component. The purpose of this is to
|
* Pass in 'children' to a React component. The purpose of this is to
|
||||||
@ -13,7 +14,7 @@ import ToolbarModule from './ToolbarModule.js';
|
|||||||
*/
|
*/
|
||||||
function withChildren(WrappedComponent, children) {
|
function withChildren(WrappedComponent, children) {
|
||||||
return function(props) {
|
return function(props) {
|
||||||
return <WrappedComponent children={children} { ...props } />;
|
return <WrappedComponent children={children} {...props} />;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,8 +22,10 @@ function withChildren(WrappedComponent, children) {
|
|||||||
// https://github.com/whitecolor/yalc
|
// https://github.com/whitecolor/yalc
|
||||||
|
|
||||||
export default class OHIFVTKExtension {
|
export default class OHIFVTKExtension {
|
||||||
constructor(children) {
|
constructor({ children, commandsManager }) {
|
||||||
this.children = children;
|
this.children = children;
|
||||||
|
|
||||||
|
_registerCommands(commandsManager, definitions, this.getExtensionId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,3 +55,21 @@ export default class OHIFVTKExtension {
|
|||||||
return ToolbarModule;
|
return ToolbarModule;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register all Viewer commands
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function _registerCommands(commandsManager, definitions, commandContext) {
|
||||||
|
commandsManager.createContext(commandContext);
|
||||||
|
Object.keys(definitions).forEach(commandName => {
|
||||||
|
const commandDefinition = definitions[commandName];
|
||||||
|
|
||||||
|
commandsManager.registerCommand(
|
||||||
|
commandContext,
|
||||||
|
commandName,
|
||||||
|
commandDefinition
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@ -1,10 +1,16 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import OHIF from 'ohif-core';
|
import OHIF from 'ohif-core';
|
||||||
import ConnectedVTKViewport from './ConnectedVTKViewport';
|
|
||||||
import cornerstone from 'cornerstone-core';
|
import cornerstone from 'cornerstone-core';
|
||||||
import handleSegmentationStorage from './handleSegmentationStorage.js';
|
|
||||||
import { getImageData, loadImageData } from 'react-vtkjs-viewport';
|
import { getImageData, loadImageData } from 'react-vtkjs-viewport';
|
||||||
|
|
||||||
|
import vtkVolume from 'vtk.js/Sources/Rendering/Core/Volume';
|
||||||
|
import vtkVolumeMapper from 'vtk.js/Sources/Rendering/Core/VolumeMapper';
|
||||||
|
import vtkImageData from 'vtk.js/Sources/Common/DataModel/ImageData';
|
||||||
|
import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray';
|
||||||
|
|
||||||
|
import ConnectedVTKViewport from './ConnectedVTKViewport';
|
||||||
|
import handleSegmentationStorage from './handleSegmentationStorage.js';
|
||||||
import LoadingIndicator from './LoadingIndicator.js';
|
import LoadingIndicator from './LoadingIndicator.js';
|
||||||
|
|
||||||
const { StackManager } = OHIF.utils;
|
const { StackManager } = OHIF.utils;
|
||||||
@ -27,9 +33,36 @@ specialCaseHandlers[
|
|||||||
SOP_CLASSES.SEGMENTATION_STORAGE
|
SOP_CLASSES.SEGMENTATION_STORAGE
|
||||||
] = handleSegmentationStorage;
|
] = handleSegmentationStorage;
|
||||||
|
|
||||||
|
// TODO: Figure out where we plan to put this long term
|
||||||
|
const volumeCache = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a labelmap image with the same dimensions as our background volume.
|
||||||
|
*
|
||||||
|
* @param backgroundImageData vtkImageData
|
||||||
|
*/
|
||||||
|
function createLabelMapImageData(backgroundImageData) {
|
||||||
|
const labelMapData = vtkImageData.newInstance(
|
||||||
|
backgroundImageData.get('spacing', 'origin', 'direction')
|
||||||
|
);
|
||||||
|
labelMapData.setDimensions(backgroundImageData.getDimensions());
|
||||||
|
labelMapData.computeTransforms();
|
||||||
|
|
||||||
|
const values = new Uint8Array(backgroundImageData.getNumberOfPoints());
|
||||||
|
const dataArray = vtkDataArray.newInstance({
|
||||||
|
numberOfComponents: 1, // labelmap with single component
|
||||||
|
values
|
||||||
|
});
|
||||||
|
labelMapData.getPointData().setScalars(dataArray);
|
||||||
|
|
||||||
|
return labelMapData;
|
||||||
|
}
|
||||||
|
|
||||||
class OHIFVTKViewport extends Component {
|
class OHIFVTKViewport extends Component {
|
||||||
state = {
|
state = {
|
||||||
viewportData: null
|
volumes: null,
|
||||||
|
paintFilterLabelMapImageData: null,
|
||||||
|
paintFilterBackgroundImageData: null
|
||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -39,14 +72,14 @@ class OHIFVTKViewport extends Component {
|
|||||||
children: PropTypes.node
|
children: PropTypes.node
|
||||||
};
|
};
|
||||||
|
|
||||||
static id = 'OHIFCornerstoneViewport';
|
static id = 'OHIFVTKViewport';
|
||||||
|
|
||||||
static init() {
|
static init() {
|
||||||
console.log('OHIFCornerstoneViewport init()');
|
console.log('OHIFVTKViewport init()');
|
||||||
}
|
}
|
||||||
|
|
||||||
static destroy() {
|
static destroy() {
|
||||||
console.log('OHIFCornerstoneViewport destroy()');
|
console.log('OHIFVTKViewport destroy()');
|
||||||
StackManager.clearStacks();
|
StackManager.clearStacks();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,22 +130,6 @@ class OHIFVTKViewport extends Component {
|
|||||||
return stack;
|
return stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
static getViewportData = (
|
|
||||||
studies,
|
|
||||||
studyInstanceUid,
|
|
||||||
displaySetInstanceUid,
|
|
||||||
sopInstanceUid,
|
|
||||||
frameIndex
|
|
||||||
) => {
|
|
||||||
return OHIFVTKViewport.getCornerstoneStack(
|
|
||||||
studies,
|
|
||||||
studyInstanceUid,
|
|
||||||
displaySetInstanceUid,
|
|
||||||
sopInstanceUid,
|
|
||||||
frameIndex
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
getViewportData = (
|
getViewportData = (
|
||||||
studies,
|
studies,
|
||||||
studyInstanceUid,
|
studyInstanceUid,
|
||||||
@ -121,63 +138,64 @@ class OHIFVTKViewport extends Component {
|
|||||||
sopInstanceUid,
|
sopInstanceUid,
|
||||||
frameIndex
|
frameIndex
|
||||||
) => {
|
) => {
|
||||||
return new Promise((resolve, reject) => {
|
const stack = OHIFVTKViewport.getCornerstoneStack(
|
||||||
const stack = OHIFVTKViewport.getViewportData(
|
studies,
|
||||||
studies,
|
studyInstanceUid,
|
||||||
studyInstanceUid,
|
displaySetInstanceUid,
|
||||||
displaySetInstanceUid,
|
sopClassUid,
|
||||||
sopClassUid,
|
sopInstanceUid,
|
||||||
sopInstanceUid,
|
frameIndex
|
||||||
frameIndex
|
);
|
||||||
);
|
|
||||||
|
|
||||||
let imageDataObject;
|
let imageDataObject;
|
||||||
let labelmapDataObject;
|
let labelmapDataObject;
|
||||||
let doneLoadingCallback;
|
|
||||||
let callbacks;
|
|
||||||
|
|
||||||
switch (sopClassUid) {
|
switch (sopClassUid) {
|
||||||
case SOP_CLASSES.SEGMENTATION_STORAGE:
|
case SOP_CLASSES.SEGMENTATION_STORAGE:
|
||||||
throw new Error("Not yet implemented");
|
throw new Error('Not yet implemented');
|
||||||
|
|
||||||
const data = handleSegmentationStorage(stack.imageIds, displaySetInstanceUid, cornerstone);
|
const data = handleSegmentationStorage(
|
||||||
imageDataObject = data.referenceDataObject;
|
stack.imageIds,
|
||||||
labelmapDataObject = data.labelmapDataObject;
|
displaySetInstanceUid
|
||||||
|
);
|
||||||
|
|
||||||
doneLoadingCallback = () => {
|
imageDataObject = data.referenceDataObject;
|
||||||
resolve({
|
labelmapDataObject = data.labelmapDataObject;
|
||||||
data: imageDataObject.vtkImageData,
|
|
||||||
labelmap: labelmapDataObject
|
return loadImageData(imageDataObject).then(() => {
|
||||||
});
|
return {
|
||||||
|
data: imageDataObject.vtkImageData,
|
||||||
|
labelmap: labelmapDataObject
|
||||||
};
|
};
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
imageDataObject = getImageData(stack.imageIds, displaySetInstanceUid);
|
||||||
|
|
||||||
callbacks = [
|
return loadImageData(imageDataObject).then(() => {
|
||||||
doneLoadingCallback
|
return {
|
||||||
];
|
data: imageDataObject.vtkImageData
|
||||||
|
|
||||||
loadImageData(imageDataObject, callbacks, cornerstone);
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
imageDataObject = getImageData(stack.imageIds, displaySetInstanceUid, cornerstone);
|
|
||||||
doneLoadingCallback = () => {
|
|
||||||
resolve({
|
|
||||||
data: imageDataObject.vtkImageData,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
});
|
||||||
callbacks = [
|
}
|
||||||
doneLoadingCallback
|
|
||||||
];
|
|
||||||
|
|
||||||
loadImageData(imageDataObject, callbacks, cornerstone);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
setStateFromProps() {
|
getOrCreateVolume(data, displaySetInstanceUid) {
|
||||||
|
if (volumeCache[displaySetInstanceUid]) {
|
||||||
|
return volumeCache[displaySetInstanceUid];
|
||||||
|
}
|
||||||
|
|
||||||
|
const volumeActor = vtkVolume.newInstance();
|
||||||
|
const volumeMapper = vtkVolumeMapper.newInstance();
|
||||||
|
|
||||||
|
volumeActor.setMapper(volumeMapper);
|
||||||
|
volumeMapper.setInputData(data);
|
||||||
|
|
||||||
|
volumeCache[displaySetInstanceUid] = volumeActor;
|
||||||
|
|
||||||
|
return volumeActor;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setStateFromProps() {
|
||||||
const { studies, displaySet } = this.props.viewportData;
|
const { studies, displaySet } = this.props.viewportData;
|
||||||
const {
|
const {
|
||||||
studyInstanceUid,
|
studyInstanceUid,
|
||||||
@ -195,18 +213,25 @@ class OHIFVTKViewport extends Component {
|
|||||||
|
|
||||||
const sopClassUid = sopClassUids[0];
|
const sopClassUid = sopClassUids[0];
|
||||||
|
|
||||||
this.getViewportData(
|
let { data, labelmap } = await this.getViewportData(
|
||||||
studies,
|
studies,
|
||||||
studyInstanceUid,
|
studyInstanceUid,
|
||||||
displaySetInstanceUid,
|
displaySetInstanceUid,
|
||||||
sopClassUid,
|
sopClassUid,
|
||||||
sopInstanceUid,
|
sopInstanceUid,
|
||||||
frameIndex
|
frameIndex
|
||||||
).then(({ data, labelmap })=> {
|
);
|
||||||
this.setState({
|
|
||||||
viewportData: data,
|
if (!labelmap) {
|
||||||
labelmap
|
labelmap = createLabelMapImageData(data);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
const volumeActor = this.getOrCreateVolume(data, displaySetInstanceUid);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
volumes: [volumeActor],
|
||||||
|
paintFilterBackgroundImageData: data,
|
||||||
|
paintFilterLabelMapImageData: labelmap
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,15 +270,22 @@ class OHIFVTKViewport extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{this.state.viewportData ? (
|
{this.state.volumes ? (
|
||||||
<ConnectedVTKViewport
|
<ConnectedVTKViewport
|
||||||
data={this.state.viewportData}
|
volumes={this.state.volumes}
|
||||||
labelmap={this.state.labelmap}
|
paintFilterLabelMapImageData={
|
||||||
|
this.state.paintFilterLabelMapImageData
|
||||||
|
}
|
||||||
|
paintFilterBackgroundImageData={
|
||||||
|
this.state.paintFilterBackgroundImageData
|
||||||
|
}
|
||||||
viewportIndex={this.props.viewportIndex}
|
viewportIndex={this.props.viewportIndex}
|
||||||
/>
|
/>
|
||||||
) : <div style={style}>
|
) : (
|
||||||
<LoadingIndicator/>
|
<div style={style}>
|
||||||
</div>}
|
<LoadingIndicator />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{childrenWithProps}
|
{childrenWithProps}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
273
extensions/ohif-vtk-extension/src/commands.js
Normal file
273
extensions/ohif-vtk-extension/src/commands.js
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
import {
|
||||||
|
vtkInteractorStyleMPRWindowLevel,
|
||||||
|
vtkInteractorStyleMPRSlice,
|
||||||
|
vtkInteractorStyleMPRCrosshairs
|
||||||
|
} from 'react-vtkjs-viewport';
|
||||||
|
|
||||||
|
import setViewportToVTK from './utils/setViewportToVTK.js';
|
||||||
|
import setMPRLayout from './utils/setMPRLayout.js';
|
||||||
|
import vtkMath from 'vtk.js/Sources/Common/Core/Math';
|
||||||
|
import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder';
|
||||||
|
|
||||||
|
// TODO: Should be another way to get this
|
||||||
|
const commandsManager = window.commandsManager;
|
||||||
|
|
||||||
|
// TODO: Put this somewhere else
|
||||||
|
let apis = {};
|
||||||
|
|
||||||
|
function getCrosshairCallbackForIndex(index) {
|
||||||
|
return worldPos => {
|
||||||
|
// Set camera focal point to world coordinate for linked views
|
||||||
|
apis.forEach((api, viewportIndex) => {
|
||||||
|
if (viewportIndex === index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are basically doing the same as getSlice but with the world coordinate
|
||||||
|
// that we want to jump to instead of the camera focal point.
|
||||||
|
// I would rather do the camera adjustment directly but I keep
|
||||||
|
// doing it wrong and so this is good enough for now.
|
||||||
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
||||||
|
const istyle = renderWindow.getInteractor().getInteractorStyle();
|
||||||
|
const sliceNormal = istyle.getSliceNormal();
|
||||||
|
const transform = vtkMatrixBuilder
|
||||||
|
.buildFromDegree()
|
||||||
|
.identity()
|
||||||
|
.rotateFromDirections(sliceNormal, [1, 0, 0]);
|
||||||
|
|
||||||
|
const mutatedWorldPos = worldPos.slice();
|
||||||
|
transform.apply(mutatedWorldPos);
|
||||||
|
const slice = mutatedWorldPos[0];
|
||||||
|
|
||||||
|
istyle.setSlice(slice);
|
||||||
|
|
||||||
|
renderWindow.render();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _getActiveViewportVTKApi(viewports) {
|
||||||
|
const { layout, viewportSpecificData, activeViewportIndex } = viewports;
|
||||||
|
|
||||||
|
const currentData = layout.viewports[activeViewportIndex];
|
||||||
|
if (currentData && currentData.plugin === 'vtk') {
|
||||||
|
// TODO: I was storing/pulling this from Redux but ran into weird issues
|
||||||
|
if (apis[activeViewportIndex]) {
|
||||||
|
return apis[activeViewportIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const displaySet = viewportSpecificData[activeViewportIndex];
|
||||||
|
|
||||||
|
let api;
|
||||||
|
if (!api) {
|
||||||
|
try {
|
||||||
|
api = await setViewportToVTK(
|
||||||
|
displaySet,
|
||||||
|
activeViewportIndex,
|
||||||
|
layout,
|
||||||
|
viewportSpecificData
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _setView(api, sliceNormal, viewUp) {
|
||||||
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
||||||
|
const renderer = api.genericRenderWindow.getRenderer();
|
||||||
|
const camera = renderer.getActiveCamera();
|
||||||
|
const istyle = renderWindow.getInteractor().getInteractorStyle();
|
||||||
|
istyle.setSliceNormal(...sliceNormal);
|
||||||
|
camera.setViewUp(...viewUp);
|
||||||
|
|
||||||
|
api.volumes[0].getMapper().setSampleDistance(5.0);
|
||||||
|
api.volumes[0].getMapper().setMaximumSamplesPerRay(2000);
|
||||||
|
|
||||||
|
renderWindow.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchMPRInteractors(api, istyle) {
|
||||||
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
||||||
|
|
||||||
|
let currentNormal;
|
||||||
|
if (istyle.getSliceNormal) {
|
||||||
|
currentNormal = istyle.getSliceNormal();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This is a hacky workaround because disabling the vtkInteractorStyleMPRSlice is currently
|
||||||
|
// broken. The camera.onModified is never removed. (https://github.com/Kitware/vtk-js/issues/1110)
|
||||||
|
renderWindow
|
||||||
|
.getInteractor()
|
||||||
|
.getInteractorStyle()
|
||||||
|
.setInteractor(null);
|
||||||
|
|
||||||
|
renderWindow.getInteractor().setInteractorStyle(istyle);
|
||||||
|
|
||||||
|
// TODO: Not sure why this is required the second time this function is called
|
||||||
|
istyle.setInteractor(renderWindow.getInteractor());
|
||||||
|
|
||||||
|
if (istyle.getVolumeMapper() !== api.volumes[0]) {
|
||||||
|
if (currentNormal) {
|
||||||
|
istyle.setSliceNormal(currentNormal);
|
||||||
|
}
|
||||||
|
istyle.setVolumeMapper(api.volumes[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
axial: async ({ viewports }) => {
|
||||||
|
const api = await _getActiveViewportVTKApi(viewports);
|
||||||
|
|
||||||
|
apis[viewports.activeViewportIndex] = api;
|
||||||
|
|
||||||
|
_setView(api, [0, 0, 1], [0, -1, 0]);
|
||||||
|
},
|
||||||
|
sagittal: async ({ viewports }) => {
|
||||||
|
const api = await _getActiveViewportVTKApi(viewports);
|
||||||
|
|
||||||
|
apis[viewports.activeViewportIndex] = api;
|
||||||
|
|
||||||
|
_setView(api, [1, 0, 0], [0, 0, 1]);
|
||||||
|
},
|
||||||
|
coronal: async ({ viewports }) => {
|
||||||
|
const api = await _getActiveViewportVTKApi(viewports);
|
||||||
|
|
||||||
|
apis[viewports.activeViewportIndex] = api;
|
||||||
|
|
||||||
|
_setView(api, [0, 1, 0], [0, 0, 1]);
|
||||||
|
},
|
||||||
|
enableRotateTool: async ({ viewports }) => {
|
||||||
|
apis.forEach(api => {
|
||||||
|
const istyle = vtkInteractorStyleMPRSlice.newInstance();
|
||||||
|
|
||||||
|
switchMPRInteractors(api, istyle);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
enableCrosshairsTool: async ({ viewports }) => {
|
||||||
|
apis.forEach((api, index) => {
|
||||||
|
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
|
||||||
|
|
||||||
|
switchMPRInteractors(api, istyle);
|
||||||
|
|
||||||
|
istyle.setCallback(getCrosshairCallbackForIndex(index));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
enableLevelTool: async ({ viewports }) => {
|
||||||
|
apis.forEach(api => {
|
||||||
|
const istyle = vtkInteractorStyleMPRWindowLevel.newInstance();
|
||||||
|
|
||||||
|
switchMPRInteractors(api, istyle);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
mpr2d: async ({ viewports }) => {
|
||||||
|
const displaySet =
|
||||||
|
viewports.viewportSpecificData[viewports.activeViewportIndex];
|
||||||
|
|
||||||
|
let apiByViewport;
|
||||||
|
try {
|
||||||
|
apiByViewport = await setMPRLayout(displaySet);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
apis = apiByViewport;
|
||||||
|
|
||||||
|
/*const rgbTransferFunction = apiByViewport[0].volumes[0]
|
||||||
|
.getProperty()
|
||||||
|
.getRGBTransferFunction(0);
|
||||||
|
rgbTransferFunction.onModified(() => {
|
||||||
|
apiByViewport.forEach(a => {
|
||||||
|
const renderWindow = a.genericRenderWindow.getRenderWindow();
|
||||||
|
|
||||||
|
renderWindow.render();
|
||||||
|
});
|
||||||
|
});*/
|
||||||
|
|
||||||
|
apis[0].volumes[0].getMapper().setSampleDistance(1.5);
|
||||||
|
|
||||||
|
apiByViewport.forEach((api, index) => {
|
||||||
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
||||||
|
const renderer = api.genericRenderWindow.getRenderer();
|
||||||
|
const camera = renderer.getActiveCamera();
|
||||||
|
|
||||||
|
// TODO: This is a hacky workaround because disabling the vtkInteractorStyleMPRSlice is currently
|
||||||
|
// broken. The camera.onModified is never removed. (https://github.com/Kitware/vtk-js/issues/1110)
|
||||||
|
renderWindow
|
||||||
|
.getInteractor()
|
||||||
|
.getInteractorStyle()
|
||||||
|
.setInteractor(null);
|
||||||
|
|
||||||
|
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
|
||||||
|
renderWindow.getInteractor().setInteractorStyle(istyle);
|
||||||
|
|
||||||
|
istyle.setVolumeMapper(api.volumes[0]);
|
||||||
|
istyle.setCallback(getCrosshairCallbackForIndex(index));
|
||||||
|
|
||||||
|
switch (index) {
|
||||||
|
default:
|
||||||
|
case 0:
|
||||||
|
//Axial
|
||||||
|
istyle.setSliceNormal(0, 0, 1);
|
||||||
|
camera.setViewUp(0, -1, 0);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
// sagittal
|
||||||
|
istyle.setSliceNormal(1, 0, 0);
|
||||||
|
camera.setViewUp(0, 0, 1);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
// Coronal
|
||||||
|
istyle.setSliceNormal(0, 1, 0);
|
||||||
|
camera.setViewUp(0, 0, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderWindow.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const definitions = {
|
||||||
|
axial: {
|
||||||
|
commandFn: actions.axial,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: {}
|
||||||
|
},
|
||||||
|
coronal: {
|
||||||
|
commandFn: actions.coronal,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: {}
|
||||||
|
},
|
||||||
|
sagittal: {
|
||||||
|
commandFn: actions.sagittal,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: {}
|
||||||
|
},
|
||||||
|
enableRotateTool: {
|
||||||
|
commandFn: actions.enableRotateTool,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: {}
|
||||||
|
},
|
||||||
|
enableCrosshairsTool: {
|
||||||
|
commandFn: actions.enableCrosshairsTool,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: {}
|
||||||
|
},
|
||||||
|
enableLevelTool: {
|
||||||
|
commandFn: actions.enableLevelTool,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: {}
|
||||||
|
},
|
||||||
|
mpr2d: {
|
||||||
|
commandFn: actions.mpr2d,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export { definitions };
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import OHIF from 'ohif-core';
|
import OHIF from 'ohif-core';
|
||||||
import * as dcmjs from 'dcmjs';
|
import * as dcmjs from 'dcmjs';
|
||||||
|
import { api } from 'dicomweb-client';
|
||||||
|
|
||||||
const { StackManager } = OHIF.utils;
|
const { StackManager } = OHIF.utils;
|
||||||
|
|
||||||
@ -47,12 +48,25 @@ function getCornerstoneStack(studies, studyInstanceUid, displaySetInstanceUid) {
|
|||||||
return stackClone;
|
return stackClone;
|
||||||
}
|
}
|
||||||
|
|
||||||
function retrieveDicomData(wadoUri) {
|
function retrieveDicomData(
|
||||||
// TODO: Authorization header depends on the server. If we ever have multiple servers
|
studyInstanceUID,
|
||||||
// we will need to figure out how / when to pass this information in.
|
seriesInstanceUID,
|
||||||
return fetch(wadoUri, {
|
sopInstanceUID,
|
||||||
headers: OHIF.DICOMWeb.getAuthorizationHeader()
|
wadoRoot
|
||||||
}).then(response => response.arrayBuffer());
|
) {
|
||||||
|
const config = {
|
||||||
|
url: wadoRoot,
|
||||||
|
headers: DICOMWeb.getAuthorizationHeader()
|
||||||
|
};
|
||||||
|
|
||||||
|
const dicomWeb = new api.DICOMwebClient(config);
|
||||||
|
const options = {
|
||||||
|
studyInstanceUID,
|
||||||
|
seriesInstanceUID,
|
||||||
|
sopInstanceUID
|
||||||
|
};
|
||||||
|
|
||||||
|
return dicomWeb.retrieveInstance(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSegmentationStorage(
|
async function handleSegmentationStorage(
|
||||||
@ -68,8 +82,22 @@ async function handleSegmentationStorage(
|
|||||||
studyInstanceUid,
|
studyInstanceUid,
|
||||||
displaySetInstanceUid
|
displaySetInstanceUid
|
||||||
);
|
);
|
||||||
const segWadoUri = displaySet.images[0].getData().wadouri;
|
|
||||||
const arrayBuffer = await retrieveDicomData(segWadoUri);
|
// TODO: This is terrible but we need to use WADO-RS or we can't retrieve the SEG
|
||||||
|
// from google cloud
|
||||||
|
const wadoRoot = displaySet.images[0].getData().wadoRoot;
|
||||||
|
|
||||||
|
const StudyInstanceUID = displaySet.images[0].getStudyInstanceUID();
|
||||||
|
const SeriesInstanceUID = displaySet.images[0].getSeriesInstanceUID();
|
||||||
|
const SOPInstanceUID = displaySet.images[0].getSOPInstanceUID();
|
||||||
|
|
||||||
|
const arrayBuffer = await retrieveDicomData(
|
||||||
|
StudyInstanceUID,
|
||||||
|
SeriesInstanceUID,
|
||||||
|
SOPInstanceUID,
|
||||||
|
wadoRoot
|
||||||
|
);
|
||||||
|
|
||||||
const dicomData = dcmjs.data.DicomMessage.readFile(arrayBuffer);
|
const dicomData = dcmjs.data.DicomMessage.readFile(arrayBuffer);
|
||||||
const dataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset(
|
const dataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset(
|
||||||
dicomData.dict
|
dicomData.dict
|
||||||
@ -109,7 +137,7 @@ async function handleSegmentationStorage(
|
|||||||
return {
|
return {
|
||||||
referenceDataObject,
|
referenceDataObject,
|
||||||
labelmapDataObject
|
labelmapDataObject
|
||||||
}
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
studyInstanceUid,
|
studyInstanceUid,
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { redux } from 'ohif-core';
|
||||||
|
|
||||||
|
const { setViewportLayoutAndData } = redux.actions;
|
||||||
|
|
||||||
|
// TODO: Should not be getting dispatch from the window, but I'm not sure how else to do it cleanly
|
||||||
|
export default function setLayoutAndViewportData(layout, viewportSpecificData) {
|
||||||
|
const action = setViewportLayoutAndData(layout, viewportSpecificData);
|
||||||
|
|
||||||
|
window.store.dispatch(action);
|
||||||
|
}
|
||||||
61
extensions/ohif-vtk-extension/src/utils/setMPRLayout.js
Normal file
61
extensions/ohif-vtk-extension/src/utils/setMPRLayout.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import setLayoutAndViewportData from './setLayoutAndViewportData.js';
|
||||||
|
import setSingleLayoutData from './setSingleLayoutData.js';
|
||||||
|
|
||||||
|
export default function setMPRLayout(displaySet) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let viewports = [];
|
||||||
|
const rows = 1;
|
||||||
|
const columns = 3;
|
||||||
|
const numViewports = rows * columns;
|
||||||
|
const viewportSpecificData = {};
|
||||||
|
for (let i = 0; i < numViewports; i++) {
|
||||||
|
viewports.push({
|
||||||
|
height: `${100 / rows}%`,
|
||||||
|
width: `${100 / columns}%`
|
||||||
|
});
|
||||||
|
|
||||||
|
viewportSpecificData[i] = displaySet;
|
||||||
|
viewportSpecificData[i].plugin = 'vtk';
|
||||||
|
}
|
||||||
|
const layout = {
|
||||||
|
viewports
|
||||||
|
};
|
||||||
|
|
||||||
|
const viewportIndices = [0, 1, 2];
|
||||||
|
let updatedViewports = layout.viewports;
|
||||||
|
|
||||||
|
const apis = [];
|
||||||
|
viewportIndices.forEach(viewportIndex => {
|
||||||
|
apis[viewportIndex] = null;
|
||||||
|
/*const currentData = layout.viewports[viewportIndex];
|
||||||
|
if (currentData && currentData.plugin === 'vtk') {
|
||||||
|
reject(new Error('Should not have reached this point??'));
|
||||||
|
}*/
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
plugin: 'vtk',
|
||||||
|
vtk: {
|
||||||
|
mode: 'mpr', // TODO: not used
|
||||||
|
afterCreation: api => {
|
||||||
|
apis[viewportIndex] = api;
|
||||||
|
|
||||||
|
if (apis.every(a => !!a)) {
|
||||||
|
resolve(apis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
updatedViewports = setSingleLayoutData(
|
||||||
|
updatedViewports,
|
||||||
|
viewportIndex,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
setLayoutAndViewportData(
|
||||||
|
{ viewports: updatedViewports },
|
||||||
|
viewportSpecificData
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
export default function setSingleLayoutData(
|
||||||
|
originalArray,
|
||||||
|
viewportIndex,
|
||||||
|
data
|
||||||
|
) {
|
||||||
|
const viewports = originalArray.slice();
|
||||||
|
const layoutData = Object.assign({}, viewports[viewportIndex], data);
|
||||||
|
|
||||||
|
viewports[viewportIndex] = layoutData;
|
||||||
|
|
||||||
|
return viewports;
|
||||||
|
}
|
||||||
39
extensions/ohif-vtk-extension/src/utils/setViewportToVTK.js
Normal file
39
extensions/ohif-vtk-extension/src/utils/setViewportToVTK.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import setLayoutAndViewportData from './setLayoutAndViewportData.js';
|
||||||
|
import setSingleLayoutData from './setSingleLayoutData.js';
|
||||||
|
|
||||||
|
export default function setViewportToVTK(
|
||||||
|
displaySet,
|
||||||
|
viewportIndex,
|
||||||
|
layout,
|
||||||
|
viewportSpecificData
|
||||||
|
) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
/*const currentData = layout.viewports[viewportIndex];
|
||||||
|
if (currentData && currentData.plugin === 'vtk') {
|
||||||
|
reject(new Error('Should not have reached this point??'));
|
||||||
|
}*/
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
plugin: 'vtk',
|
||||||
|
vtk: {
|
||||||
|
mode: 'mpr', // TODO: not used
|
||||||
|
afterCreation: api => {
|
||||||
|
resolve(api);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updatedViewports = setSingleLayoutData(
|
||||||
|
layout.viewports,
|
||||||
|
viewportIndex,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
const updatedViewportData = viewportSpecificData;
|
||||||
|
|
||||||
|
setLayoutAndViewportData(
|
||||||
|
{ viewports: updatedViewports },
|
||||||
|
updatedViewportData
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
108
extensions/ohif-vtk-extension/webpack.config.js
Normal file
108
extensions/ohif-vtk-extension/webpack.config.js
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
var path = require('path')
|
||||||
|
var webpack = require('webpack')
|
||||||
|
|
||||||
|
const autoprefixer = require('autoprefixer');
|
||||||
|
|
||||||
|
const cssRules = [
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
exclude: /\.module\.css$/,
|
||||||
|
use: [
|
||||||
|
'style-loader',
|
||||||
|
'css-loader',
|
||||||
|
{
|
||||||
|
loader: 'postcss-loader',
|
||||||
|
options: {
|
||||||
|
plugins: () => [autoprefixer('last 2 version', 'ie >= 10')],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.glsl$/i,
|
||||||
|
include: /vtk\.js[\/\\]Sources/,
|
||||||
|
loader: 'shader-loader',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.worker\.js$/,
|
||||||
|
include: /vtk\.js[\/\\]Sources/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'worker-loader',
|
||||||
|
options: { inline: true, fallback: false },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
include: /\.module\.css$/,
|
||||||
|
use: [
|
||||||
|
{ loader: 'style-loader' },
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
localIdentName: '[name]-[local]_[sha512:hash:base64:5]',
|
||||||
|
modules: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: 'postcss-loader',
|
||||||
|
options: {
|
||||||
|
plugins: () => [autoprefixer('last 2 version', 'ie >= 10')],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
var entry = path.join(__dirname, './src/index.js')
|
||||||
|
const sourcePath = path.join(__dirname, './src')
|
||||||
|
const outputPath = path.join(__dirname, './dist')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry,
|
||||||
|
output: {
|
||||||
|
path: outputPath,
|
||||||
|
filename: 'index.umd.js',
|
||||||
|
library: '@ohif/extension-vtk',
|
||||||
|
libraryTarget: 'umd',
|
||||||
|
globalObject: 'this',
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.(js|jsx)$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: ['babel-loader'],
|
||||||
|
}
|
||||||
|
].concat(cssRules),
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
modules: [path.resolve(__dirname, 'node_modules'), sourcePath],
|
||||||
|
},
|
||||||
|
externals: [{
|
||||||
|
'cornerstone-core': {
|
||||||
|
commonjs: 'cornerstone-core',
|
||||||
|
commonjs2: 'cornerstone-core',
|
||||||
|
amd: 'cornerstone-core',
|
||||||
|
root: 'cornerstone',
|
||||||
|
},
|
||||||
|
'cornerstone-math': {
|
||||||
|
commonjs: 'cornerstone-math',
|
||||||
|
commonjs2: 'cornerstone-math',
|
||||||
|
amd: 'cornerstone-math',
|
||||||
|
root: 'cornerstoneMath',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'ohif-core',
|
||||||
|
'dcmjs',
|
||||||
|
'react-viewerbase',
|
||||||
|
'react',//: 'React',
|
||||||
|
'react-dom',//: 'ReactDOM',
|
||||||
|
'react-redux',//: 'ReactRedux',
|
||||||
|
'react-resize-detector',//: 'ReactResizeDetector',
|
||||||
|
'react-viewerbase',//: 'reactViewerbase',
|
||||||
|
'prop-types'//: 'PropTypes'
|
||||||
|
/*/\b(vtk.js)/*/
|
||||||
|
],
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user