From 559b637290dd19548db2f17d9474b11403e0fcbc Mon Sep 17 00:00:00 2001 From: "James A. Petts" Date: Thu, 3 Oct 2019 14:29:56 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Synced=20Window=20Leveli?= =?UTF-8?q?ng?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Syncs window leveling for vtkjs viewports in MPR 2D mode. Closes: #558 --- extensions/vtk/src/OHIFVTKViewport.js | 76 +++++++++++-------- extensions/vtk/src/commandsModule.js | 10 ++- .../utils/vtkViewportSubscriptionManager.js | 28 +++++++ yarn.lock | 10 +++ 4 files changed, 89 insertions(+), 35 deletions(-) create mode 100644 extensions/vtk/src/utils/vtkViewportSubscriptionManager.js diff --git a/extensions/vtk/src/OHIFVTKViewport.js b/extensions/vtk/src/OHIFVTKViewport.js index ae1dd2c0d..3a6e53e71 100644 --- a/extensions/vtk/src/OHIFVTKViewport.js +++ b/extensions/vtk/src/OHIFVTKViewport.js @@ -1,16 +1,17 @@ -import React, { Component } from "react"; -import { getImageData, loadImageData } from "react-vtkjs-viewport"; +import React, { Component } from 'react'; +import { getImageData, loadImageData } from 'react-vtkjs-viewport'; -import ConnectedVTKViewport from "./ConnectedVTKViewport"; -import LoadingIndicator from "./LoadingIndicator.js"; -import OHIF from "@ohif/core"; -import PropTypes from "prop-types"; -import cornerstone from "cornerstone-core"; -import handleSegmentationStorage from "./handleSegmentationStorage.js"; -import vtkDataArray from "vtk.js/Sources/Common/Core/DataArray"; -import vtkImageData from "vtk.js/Sources/Common/DataModel/ImageData"; -import vtkVolume from "vtk.js/Sources/Rendering/Core/Volume"; -import vtkVolumeMapper from "vtk.js/Sources/Rendering/Core/VolumeMapper"; +import ConnectedVTKViewport from './ConnectedVTKViewport'; +import LoadingIndicator from './LoadingIndicator.js'; +import OHIF from '@ohif/core'; +import PropTypes from 'prop-types'; +import cornerstone from 'cornerstone-core'; +import handleSegmentationStorage from './handleSegmentationStorage.js'; +import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray'; +import vtkImageData from 'vtk.js/Sources/Common/DataModel/ImageData'; +import vtkVolume from 'vtk.js/Sources/Rendering/Core/Volume'; +import vtkVolumeMapper from 'vtk.js/Sources/Rendering/Core/VolumeMapper'; +import vtkViewportSubscriptionManager from './utils/vtkViewportSubscriptionManager.js'; const { StackManager } = OHIF.utils; @@ -24,7 +25,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 = {}; @@ -42,7 +43,7 @@ const volumeCache = {}; */ function createLabelMapImageData(backgroundImageData) { const labelMapData = vtkImageData.newInstance( - backgroundImageData.get("spacing", "origin", "direction") + backgroundImageData.get('spacing', 'origin', 'direction') ); labelMapData.setDimensions(backgroundImageData.getDimensions()); labelMapData.computeTransforms(); @@ -50,7 +51,7 @@ function createLabelMapImageData(backgroundImageData) { const values = new Uint8Array(backgroundImageData.getNumberOfPoints()); const dataArray = vtkDataArray.newInstance({ numberOfComponents: 1, // labelmap with single component - values + values, }); labelMapData.getPointData().setScalars(dataArray); @@ -61,24 +62,24 @@ class OHIFVTKViewport extends Component { state = { volumes: null, paintFilterLabelMapImageData: null, - paintFilterBackgroundImageData: null + paintFilterBackgroundImageData: null, }; static propTypes = { studies: PropTypes.object, displaySet: PropTypes.object, viewportIndex: PropTypes.number, - children: PropTypes.node + children: PropTypes.node, }; - static id = "OHIFVTKViewport"; + static id = 'OHIFVTKViewport'; static init() { - console.log("OHIFVTKViewport init()"); + console.log('OHIFVTKViewport init()'); } static destroy() { - console.log("OHIFVTKViewport destroy()"); + console.log('OHIFVTKViewport destroy()'); StackManager.clearStacks(); } @@ -109,7 +110,7 @@ class OHIFVTKViewport extends Component { } else if (sopInstanceUid) { const index = stack.imageIds.findIndex(imageId => { const sopCommonModule = cornerstone.metaData.get( - "sopCommonModule", + 'sopCommonModule', imageId ); if (!sopCommonModule) { @@ -151,7 +152,7 @@ class OHIFVTKViewport extends Component { switch (sopClassUid) { case SOP_CLASSES.SEGMENTATION_STORAGE: - throw new Error("Not yet implemented"); + throw new Error('Not yet implemented'); const data = handleSegmentationStorage( stack.imageIds, @@ -164,7 +165,7 @@ class OHIFVTKViewport extends Component { return loadImageData(imageDataObject).then(() => { return { data: imageDataObject.vtkImageData, - labelmap: labelmapDataObject + labelmap: labelmapDataObject, }; }); default: @@ -172,7 +173,7 @@ class OHIFVTKViewport extends Component { return loadImageData(imageDataObject).then(() => { return { - data: imageDataObject.vtkImageData + data: imageDataObject.vtkImageData, }; }); } @@ -189,10 +190,16 @@ class OHIFVTKViewport extends Component { volumeActor.setMapper(volumeMapper); 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 - 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 const sampleDistance = @@ -200,7 +207,7 @@ class OHIFVTKViewport extends Component { Math.sqrt( data .getSpacing() - .map((v) => v * v) + .map(v => v * v) .reduce((a, b) => a + b, 0) ); @@ -218,12 +225,12 @@ class OHIFVTKViewport extends Component { displaySetInstanceUid, sopClassUids, sopInstanceUid, - frameIndex + frameIndex, } = displaySet; if (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.' ); } @@ -249,7 +256,7 @@ class OHIFVTKViewport extends Component { this.setState({ volumes: [volumeActor], paintFilterBackgroundImageData: data, - paintFilterLabelMapImageData: labelmap + paintFilterLabelMapImageData: labelmap, }); } @@ -257,6 +264,11 @@ class OHIFVTKViewport extends Component { this.setStateFromProps(); } + componentWillUnmount() { + console.log(this.props.viewportIndex); + vtkViewportSubscriptionManager.unsubscribe(this.props.viewportIndex); + } + componentDidUpdate(prevProps) { const { studies, displaySet } = this.props.viewportData; const prevDisplaySet = prevProps.viewportData.displaySet; @@ -279,12 +291,12 @@ class OHIFVTKViewport extends Component { childrenWithProps = this.props.children.map((child, index) => { return React.cloneElement(child, { 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 ( <> diff --git a/extensions/vtk/src/commandsModule.js b/extensions/vtk/src/commandsModule.js index dd6a3bcf2..d0c18f3d4 100644 --- a/extensions/vtk/src/commandsModule.js +++ b/extensions/vtk/src/commandsModule.js @@ -9,6 +9,7 @@ import { import setMPRLayout from './utils/setMPRLayout.js'; import setViewportToVTK from './utils/setViewportToVTK.js'; +import vtkViewportSubscriptionManager from './utils/vtkViewportSubscriptionManager.js'; import vtkCoordinate from 'vtk.js/Sources/Rendering/Core/Coordinate'; import vtkMath from 'vtk.js/Sources/Common/Core/Math'; import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder'; @@ -263,16 +264,19 @@ const actions = { apis = apiByViewport; - /*const rgbTransferFunction = apiByViewport[0].volumes[0] + const rgbTransferFunction = apiByViewport[0].volumes[0] .getProperty() .getRGBTransferFunction(0); - rgbTransferFunction.onModified(() => { + + const onModifiedSubscription = rgbTransferFunction.onModified(() => { apiByViewport.forEach(a => { const renderWindow = a.genericRenderWindow.getRenderWindow(); renderWindow.render(); }); - });*/ + }); + + vtkViewportSubscriptionManager.pushSubscription(0, onModifiedSubscription); apiByViewport.forEach((api, index) => { const renderWindow = api.genericRenderWindow.getRenderWindow(); diff --git a/extensions/vtk/src/utils/vtkViewportSubscriptionManager.js b/extensions/vtk/src/utils/vtkViewportSubscriptionManager.js new file mode 100644 index 000000000..fdd61cb2a --- /dev/null +++ b/extensions/vtk/src/utils/vtkViewportSubscriptionManager.js @@ -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; diff --git a/yarn.lock b/yarn.lock index 7095b3ea5..f4beb53ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2272,6 +2272,16 @@ once "^1.4.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": version "0.2.5" resolved "https://registry.yarnpkg.com/@ohif/i18n/-/i18n-0.2.5.tgz#2b92a78109823ce0f50dd590201d980a5fd64974"