Merge pull request #993 from JamesAPetts/vtkViewportSubscriptionManager

feat: 🎸 Synced Window Leveling
This commit is contained in:
James Petts 2019-10-03 14:49:19 +01:00 committed by GitHub
commit 7709ebb107
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 89 additions and 35 deletions

View File

@ -1,16 +1,17 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { getImageData, loadImageData } from "react-vtkjs-viewport"; import { getImageData, loadImageData } from 'react-vtkjs-viewport';
import ConnectedVTKViewport from "./ConnectedVTKViewport"; import ConnectedVTKViewport from './ConnectedVTKViewport';
import LoadingIndicator from "./LoadingIndicator.js"; import LoadingIndicator from './LoadingIndicator.js';
import OHIF from "@ohif/core"; import OHIF from '@ohif/core';
import PropTypes from "prop-types"; import PropTypes from 'prop-types';
import cornerstone from "cornerstone-core"; import cornerstone from 'cornerstone-core';
import handleSegmentationStorage from "./handleSegmentationStorage.js"; import handleSegmentationStorage from './handleSegmentationStorage.js';
import vtkDataArray from "vtk.js/Sources/Common/Core/DataArray"; import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray';
import vtkImageData from "vtk.js/Sources/Common/DataModel/ImageData"; import vtkImageData from 'vtk.js/Sources/Common/DataModel/ImageData';
import vtkVolume from "vtk.js/Sources/Rendering/Core/Volume"; import vtkVolume from 'vtk.js/Sources/Rendering/Core/Volume';
import vtkVolumeMapper from "vtk.js/Sources/Rendering/Core/VolumeMapper"; import vtkVolumeMapper from 'vtk.js/Sources/Rendering/Core/VolumeMapper';
import vtkViewportSubscriptionManager from './utils/vtkViewportSubscriptionManager.js';
const { StackManager } = OHIF.utils; const { StackManager } = OHIF.utils;
@ -24,7 +25,7 @@ cornerstone.metaData.addProvider(
StackManager.setMetadataProvider(metadataProvider); StackManager.setMetadataProvider(metadataProvider);
const SOP_CLASSES = { 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 = {}; const specialCaseHandlers = {};
@ -42,7 +43,7 @@ const volumeCache = {};
*/ */
function createLabelMapImageData(backgroundImageData) { function createLabelMapImageData(backgroundImageData) {
const labelMapData = vtkImageData.newInstance( const labelMapData = vtkImageData.newInstance(
backgroundImageData.get("spacing", "origin", "direction") backgroundImageData.get('spacing', 'origin', 'direction')
); );
labelMapData.setDimensions(backgroundImageData.getDimensions()); labelMapData.setDimensions(backgroundImageData.getDimensions());
labelMapData.computeTransforms(); labelMapData.computeTransforms();
@ -50,7 +51,7 @@ function createLabelMapImageData(backgroundImageData) {
const values = new Uint8Array(backgroundImageData.getNumberOfPoints()); const values = new Uint8Array(backgroundImageData.getNumberOfPoints());
const dataArray = vtkDataArray.newInstance({ const dataArray = vtkDataArray.newInstance({
numberOfComponents: 1, // labelmap with single component numberOfComponents: 1, // labelmap with single component
values values,
}); });
labelMapData.getPointData().setScalars(dataArray); labelMapData.getPointData().setScalars(dataArray);
@ -61,24 +62,24 @@ class OHIFVTKViewport extends Component {
state = { state = {
volumes: null, volumes: null,
paintFilterLabelMapImageData: null, paintFilterLabelMapImageData: null,
paintFilterBackgroundImageData: null paintFilterBackgroundImageData: null,
}; };
static propTypes = { static propTypes = {
studies: PropTypes.object, studies: PropTypes.object,
displaySet: PropTypes.object, displaySet: PropTypes.object,
viewportIndex: PropTypes.number, viewportIndex: PropTypes.number,
children: PropTypes.node children: PropTypes.node,
}; };
static id = "OHIFVTKViewport"; static id = 'OHIFVTKViewport';
static init() { static init() {
console.log("OHIFVTKViewport init()"); console.log('OHIFVTKViewport init()');
} }
static destroy() { static destroy() {
console.log("OHIFVTKViewport destroy()"); console.log('OHIFVTKViewport destroy()');
StackManager.clearStacks(); StackManager.clearStacks();
} }
@ -109,7 +110,7 @@ class OHIFVTKViewport extends Component {
} else if (sopInstanceUid) { } else if (sopInstanceUid) {
const index = stack.imageIds.findIndex(imageId => { const index = stack.imageIds.findIndex(imageId => {
const sopCommonModule = cornerstone.metaData.get( const sopCommonModule = cornerstone.metaData.get(
"sopCommonModule", 'sopCommonModule',
imageId imageId
); );
if (!sopCommonModule) { if (!sopCommonModule) {
@ -151,7 +152,7 @@ class OHIFVTKViewport extends Component {
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( const data = handleSegmentationStorage(
stack.imageIds, stack.imageIds,
@ -164,7 +165,7 @@ class OHIFVTKViewport extends Component {
return loadImageData(imageDataObject).then(() => { return loadImageData(imageDataObject).then(() => {
return { return {
data: imageDataObject.vtkImageData, data: imageDataObject.vtkImageData,
labelmap: labelmapDataObject labelmap: labelmapDataObject,
}; };
}); });
default: default:
@ -172,7 +173,7 @@ class OHIFVTKViewport extends Component {
return loadImageData(imageDataObject).then(() => { return loadImageData(imageDataObject).then(() => {
return { return {
data: imageDataObject.vtkImageData data: imageDataObject.vtkImageData,
}; };
}); });
} }
@ -189,10 +190,16 @@ class OHIFVTKViewport extends Component {
volumeActor.setMapper(volumeMapper); volumeActor.setMapper(volumeMapper);
volumeMapper.setInputData(data); volumeMapper.setInputData(data);
const range = data.getPointData().getScalars().getRange(); const range = data
.getPointData()
.getScalars()
.getRange();
// TODO: For PET we might want to just set this to 0-5 SUV // TODO: For PET we might want to just set this to 0-5 SUV
volumeActor.getProperty().getRGBTransferFunction(0).setRange(range[0], range[1]); volumeActor
.getProperty()
.getRGBTransferFunction(0)
.setRange(range[0], range[1]);
// TODO: Should look into implementing autoAdjustSampleDistance in vtk // TODO: Should look into implementing autoAdjustSampleDistance in vtk
const sampleDistance = const sampleDistance =
@ -200,7 +207,7 @@ class OHIFVTKViewport extends Component {
Math.sqrt( Math.sqrt(
data data
.getSpacing() .getSpacing()
.map((v) => v * v) .map(v => v * v)
.reduce((a, b) => a + b, 0) .reduce((a, b) => a + b, 0)
); );
@ -218,12 +225,12 @@ class OHIFVTKViewport extends Component {
displaySetInstanceUid, displaySetInstanceUid,
sopClassUids, sopClassUids,
sopInstanceUid, sopInstanceUid,
frameIndex frameIndex,
} = displaySet; } = displaySet;
if (sopClassUids.length > 1) { if (sopClassUids.length > 1) {
console.warn( 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.'
); );
} }
@ -249,7 +256,7 @@ class OHIFVTKViewport extends Component {
this.setState({ this.setState({
volumes: [volumeActor], volumes: [volumeActor],
paintFilterBackgroundImageData: data, paintFilterBackgroundImageData: data,
paintFilterLabelMapImageData: labelmap paintFilterLabelMapImageData: labelmap,
}); });
} }
@ -257,6 +264,11 @@ class OHIFVTKViewport extends Component {
this.setStateFromProps(); this.setStateFromProps();
} }
componentWillUnmount() {
console.log(this.props.viewportIndex);
vtkViewportSubscriptionManager.unsubscribe(this.props.viewportIndex);
}
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
const { studies, displaySet } = this.props.viewportData; const { studies, displaySet } = this.props.viewportData;
const prevDisplaySet = prevProps.viewportData.displaySet; const prevDisplaySet = prevProps.viewportData.displaySet;
@ -279,12 +291,12 @@ class OHIFVTKViewport extends Component {
childrenWithProps = this.props.children.map((child, index) => { childrenWithProps = this.props.children.map((child, index) => {
return React.cloneElement(child, { return React.cloneElement(child, {
viewportIndex: this.props.viewportIndex, viewportIndex: this.props.viewportIndex,
key: index key: index,
}); });
}); });
} }
const style = { width: "100%", height: "100%", position: "relative" }; const style = { width: '100%', height: '100%', position: 'relative' };
return ( return (
<> <>

View File

@ -9,6 +9,7 @@ import {
import setMPRLayout from './utils/setMPRLayout.js'; import setMPRLayout from './utils/setMPRLayout.js';
import setViewportToVTK from './utils/setViewportToVTK.js'; import setViewportToVTK from './utils/setViewportToVTK.js';
import vtkViewportSubscriptionManager from './utils/vtkViewportSubscriptionManager.js';
import vtkCoordinate from 'vtk.js/Sources/Rendering/Core/Coordinate'; import vtkCoordinate from 'vtk.js/Sources/Rendering/Core/Coordinate';
import vtkMath from 'vtk.js/Sources/Common/Core/Math'; import vtkMath from 'vtk.js/Sources/Common/Core/Math';
import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder'; import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder';
@ -263,16 +264,19 @@ const actions = {
apis = apiByViewport; apis = apiByViewport;
/*const rgbTransferFunction = apiByViewport[0].volumes[0] const rgbTransferFunction = apiByViewport[0].volumes[0]
.getProperty() .getProperty()
.getRGBTransferFunction(0); .getRGBTransferFunction(0);
rgbTransferFunction.onModified(() => {
const onModifiedSubscription = rgbTransferFunction.onModified(() => {
apiByViewport.forEach(a => { apiByViewport.forEach(a => {
const renderWindow = a.genericRenderWindow.getRenderWindow(); const renderWindow = a.genericRenderWindow.getRenderWindow();
renderWindow.render(); renderWindow.render();
}); });
});*/ });
vtkViewportSubscriptionManager.pushSubscription(0, onModifiedSubscription);
apiByViewport.forEach((api, index) => { apiByViewport.forEach((api, index) => {
const renderWindow = api.genericRenderWindow.getRenderWindow(); const renderWindow = api.genericRenderWindow.getRenderWindow();

View File

@ -0,0 +1,28 @@
const subscriptions = [];
// This is pretty hacky right now, but it makes sure we don't keep adding subscriptions.
// TODO -> Nuke this and move it up a layer once we have more vigorous layout support.
const vtkViewportSubscriptionManager = {
subscriptions,
pushSubscription(viewportIndex, subscription) {
if (!Array.isArray(subscriptions[viewportIndex])) {
subscriptions[viewportIndex] = [];
}
subscriptions[viewportIndex].push(subscription);
},
unsubscribe(viewportIndex) {
if (!subscriptions[viewportIndex]) {
return;
}
while (subscriptions[viewportIndex].length) {
subscriptions[viewportIndex].pop().unsubscribe();
}
subscriptions[viewportIndex] = null;
},
};
export default vtkViewportSubscriptionManager;

View File

@ -2272,6 +2272,16 @@
once "^1.4.0" once "^1.4.0"
universal-user-agent "^4.0.0" universal-user-agent "^4.0.0"
"@ohif/extension-cornerstone@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@ohif/extension-cornerstone/-/extension-cornerstone-2.0.0.tgz#b4ee3b594212502192cdd311a100354857280548"
integrity sha512-hvJ3t2GRcu906NiYpUWaJIB9ja/M/MKEYLy19t4XBFpqf4VqoezR2KJpZelmZjQscXGaDQeZgvr3DJO5Nva8Uw==
dependencies:
"@babel/runtime" "^7.5.5"
classnames "^2.2.6"
lodash.throttle "^4.1.1"
react-cornerstone-viewport "0.1.30"
"@ohif/i18n@^0.2.3": "@ohif/i18n@^0.2.3":
version "0.2.5" version "0.2.5"
resolved "https://registry.yarnpkg.com/@ohif/i18n/-/i18n-0.2.5.tgz#2b92a78109823ce0f50dd590201d980a5fd64974" resolved "https://registry.yarnpkg.com/@ohif/i18n/-/i18n-0.2.5.tgz#2b92a78109823ce0f50dd590201d980a5fd64974"