Skip to content

Commit 559b637

Browse files
committed
feat: 🎸 Synced Window Leveling
Syncs window leveling for vtkjs viewports in MPR 2D mode. Closes: #558
1 parent 36910f2 commit 559b637

4 files changed

Lines changed: 90 additions & 36 deletions

File tree

extensions/vtk/src/OHIFVTKViewport.js

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
import React, { Component } from "react";
2-
import { getImageData, loadImageData } from "react-vtkjs-viewport";
3-
4-
import ConnectedVTKViewport from "./ConnectedVTKViewport";
5-
import LoadingIndicator from "./LoadingIndicator.js";
6-
import OHIF from "@ohif/core";
7-
import PropTypes from "prop-types";
8-
import cornerstone from "cornerstone-core";
9-
import handleSegmentationStorage from "./handleSegmentationStorage.js";
10-
import vtkDataArray from "vtk.js/Sources/Common/Core/DataArray";
11-
import vtkImageData from "vtk.js/Sources/Common/DataModel/ImageData";
12-
import vtkVolume from "vtk.js/Sources/Rendering/Core/Volume";
13-
import vtkVolumeMapper from "vtk.js/Sources/Rendering/Core/VolumeMapper";
1+
import React, { Component } from 'react';
2+
import { getImageData, loadImageData } from 'react-vtkjs-viewport';
3+
4+
import ConnectedVTKViewport from './ConnectedVTKViewport';
5+
import LoadingIndicator from './LoadingIndicator.js';
6+
import OHIF from '@ohif/core';
7+
import PropTypes from 'prop-types';
8+
import cornerstone from 'cornerstone-core';
9+
import handleSegmentationStorage from './handleSegmentationStorage.js';
10+
import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray';
11+
import vtkImageData from 'vtk.js/Sources/Common/DataModel/ImageData';
12+
import vtkVolume from 'vtk.js/Sources/Rendering/Core/Volume';
13+
import vtkVolumeMapper from 'vtk.js/Sources/Rendering/Core/VolumeMapper';
14+
import vtkViewportSubscriptionManager from './utils/vtkViewportSubscriptionManager.js';
1415

1516
const { StackManager } = OHIF.utils;
1617

@@ -24,7 +25,7 @@ cornerstone.metaData.addProvider(
2425
StackManager.setMetadataProvider(metadataProvider);
2526

2627
const SOP_CLASSES = {
27-
SEGMENTATION_STORAGE: "1.2.840.10008.5.1.4.1.1.66.4"
28+
SEGMENTATION_STORAGE: '1.2.840.10008.5.1.4.1.1.66.4',
2829
};
2930

3031
const specialCaseHandlers = {};
@@ -42,15 +43,15 @@ const volumeCache = {};
4243
*/
4344
function createLabelMapImageData(backgroundImageData) {
4445
const labelMapData = vtkImageData.newInstance(
45-
backgroundImageData.get("spacing", "origin", "direction")
46+
backgroundImageData.get('spacing', 'origin', 'direction')
4647
);
4748
labelMapData.setDimensions(backgroundImageData.getDimensions());
4849
labelMapData.computeTransforms();
4950

5051
const values = new Uint8Array(backgroundImageData.getNumberOfPoints());
5152
const dataArray = vtkDataArray.newInstance({
5253
numberOfComponents: 1, // labelmap with single component
53-
values
54+
values,
5455
});
5556
labelMapData.getPointData().setScalars(dataArray);
5657

@@ -61,24 +62,24 @@ class OHIFVTKViewport extends Component {
6162
state = {
6263
volumes: null,
6364
paintFilterLabelMapImageData: null,
64-
paintFilterBackgroundImageData: null
65+
paintFilterBackgroundImageData: null,
6566
};
6667

6768
static propTypes = {
6869
studies: PropTypes.object,
6970
displaySet: PropTypes.object,
7071
viewportIndex: PropTypes.number,
71-
children: PropTypes.node
72+
children: PropTypes.node,
7273
};
7374

74-
static id = "OHIFVTKViewport";
75+
static id = 'OHIFVTKViewport';
7576

7677
static init() {
77-
console.log("OHIFVTKViewport init()");
78+
console.log('OHIFVTKViewport init()');
7879
}
7980

8081
static destroy() {
81-
console.log("OHIFVTKViewport destroy()");
82+
console.log('OHIFVTKViewport destroy()');
8283
StackManager.clearStacks();
8384
}
8485

@@ -109,7 +110,7 @@ class OHIFVTKViewport extends Component {
109110
} else if (sopInstanceUid) {
110111
const index = stack.imageIds.findIndex(imageId => {
111112
const sopCommonModule = cornerstone.metaData.get(
112-
"sopCommonModule",
113+
'sopCommonModule',
113114
imageId
114115
);
115116
if (!sopCommonModule) {
@@ -151,7 +152,7 @@ class OHIFVTKViewport extends Component {
151152

152153
switch (sopClassUid) {
153154
case SOP_CLASSES.SEGMENTATION_STORAGE:
154-
throw new Error("Not yet implemented");
155+
throw new Error('Not yet implemented');
155156

156157
const data = handleSegmentationStorage(
157158
stack.imageIds,
@@ -164,15 +165,15 @@ class OHIFVTKViewport extends Component {
164165
return loadImageData(imageDataObject).then(() => {
165166
return {
166167
data: imageDataObject.vtkImageData,
167-
labelmap: labelmapDataObject
168+
labelmap: labelmapDataObject,
168169
};
169170
});
170171
default:
171172
imageDataObject = getImageData(stack.imageIds, displaySetInstanceUid);
172173

173174
return loadImageData(imageDataObject).then(() => {
174175
return {
175-
data: imageDataObject.vtkImageData
176+
data: imageDataObject.vtkImageData,
176177
};
177178
});
178179
}
@@ -189,18 +190,24 @@ class OHIFVTKViewport extends Component {
189190
volumeActor.setMapper(volumeMapper);
190191
volumeMapper.setInputData(data);
191192

192-
const range = data.getPointData().getScalars().getRange();
193+
const range = data
194+
.getPointData()
195+
.getScalars()
196+
.getRange();
193197

194198
// TODO: For PET we might want to just set this to 0-5 SUV
195-
volumeActor.getProperty().getRGBTransferFunction(0).setRange(range[0], range[1]);
199+
volumeActor
200+
.getProperty()
201+
.getRGBTransferFunction(0)
202+
.setRange(range[0], range[1]);
196203

197204
// TODO: Should look into implementing autoAdjustSampleDistance in vtk
198205
const sampleDistance =
199206
1.2 *
200207
Math.sqrt(
201208
data
202209
.getSpacing()
203-
.map((v) => v * v)
210+
.map(v => v * v)
204211
.reduce((a, b) => a + b, 0)
205212
);
206213

@@ -218,12 +225,12 @@ class OHIFVTKViewport extends Component {
218225
displaySetInstanceUid,
219226
sopClassUids,
220227
sopInstanceUid,
221-
frameIndex
228+
frameIndex,
222229
} = displaySet;
223230

224231
if (sopClassUids.length > 1) {
225232
console.warn(
226-
"More than one SOPClassUid in the same series is not yet supported."
233+
'More than one SOPClassUid in the same series is not yet supported.'
227234
);
228235
}
229236

@@ -249,14 +256,19 @@ class OHIFVTKViewport extends Component {
249256
this.setState({
250257
volumes: [volumeActor],
251258
paintFilterBackgroundImageData: data,
252-
paintFilterLabelMapImageData: labelmap
259+
paintFilterLabelMapImageData: labelmap,
253260
});
254261
}
255262

256263
componentDidMount() {
257264
this.setStateFromProps();
258265
}
259266

267+
componentWillUnmount() {
268+
console.log(this.props.viewportIndex);
269+
vtkViewportSubscriptionManager.unsubscribe(this.props.viewportIndex);
270+
}
271+
260272
componentDidUpdate(prevProps) {
261273
const { studies, displaySet } = this.props.viewportData;
262274
const prevDisplaySet = prevProps.viewportData.displaySet;
@@ -279,12 +291,12 @@ class OHIFVTKViewport extends Component {
279291
childrenWithProps = this.props.children.map((child, index) => {
280292
return React.cloneElement(child, {
281293
viewportIndex: this.props.viewportIndex,
282-
key: index
294+
key: index,
283295
});
284296
});
285297
}
286298

287-
const style = { width: "100%", height: "100%", position: "relative" };
299+
const style = { width: '100%', height: '100%', position: 'relative' };
288300

289301
return (
290302
<>

extensions/vtk/src/commandsModule.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99

1010
import setMPRLayout from './utils/setMPRLayout.js';
1111
import setViewportToVTK from './utils/setViewportToVTK.js';
12+
import vtkViewportSubscriptionManager from './utils/vtkViewportSubscriptionManager.js';
1213
import vtkCoordinate from 'vtk.js/Sources/Rendering/Core/Coordinate';
1314
import vtkMath from 'vtk.js/Sources/Common/Core/Math';
1415
import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder';
@@ -263,16 +264,19 @@ const actions = {
263264

264265
apis = apiByViewport;
265266

266-
/*const rgbTransferFunction = apiByViewport[0].volumes[0]
267+
const rgbTransferFunction = apiByViewport[0].volumes[0]
267268
.getProperty()
268269
.getRGBTransferFunction(0);
269-
rgbTransferFunction.onModified(() => {
270+
271+
const onModifiedSubscription = rgbTransferFunction.onModified(() => {
270272
apiByViewport.forEach(a => {
271273
const renderWindow = a.genericRenderWindow.getRenderWindow();
272274

273275
renderWindow.render();
274276
});
275-
});*/
277+
});
278+
279+
vtkViewportSubscriptionManager.pushSubscription(0, onModifiedSubscription);
276280

277281
apiByViewport.forEach((api, index) => {
278282
const renderWindow = api.genericRenderWindow.getRenderWindow();
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const subscriptions = [];
2+
3+
// This is pretty hacky right now, but it makes sure we don't keep adding subscriptions.
4+
// TODO -> Nuke this and move it up a layer once we have more vigorous layout support.
5+
6+
const vtkViewportSubscriptionManager = {
7+
subscriptions,
8+
pushSubscription(viewportIndex, subscription) {
9+
if (!Array.isArray(subscriptions[viewportIndex])) {
10+
subscriptions[viewportIndex] = [];
11+
}
12+
13+
subscriptions[viewportIndex].push(subscription);
14+
},
15+
unsubscribe(viewportIndex) {
16+
if (!subscriptions[viewportIndex]) {
17+
return;
18+
}
19+
20+
while (subscriptions[viewportIndex].length) {
21+
subscriptions[viewportIndex].pop().unsubscribe();
22+
}
23+
24+
subscriptions[viewportIndex] = null;
25+
},
26+
};
27+
28+
export default vtkViewportSubscriptionManager;

yarn.lock

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2272,6 +2272,16 @@
22722272
once "^1.4.0"
22732273
universal-user-agent "^4.0.0"
22742274

2275+
"@ohif/extension-cornerstone@^2.0.0":
2276+
version "2.0.0"
2277+
resolved "https://registry.yarnpkg.com/@ohif/extension-cornerstone/-/extension-cornerstone-2.0.0.tgz#b4ee3b594212502192cdd311a100354857280548"
2278+
integrity sha512-hvJ3t2GRcu906NiYpUWaJIB9ja/M/MKEYLy19t4XBFpqf4VqoezR2KJpZelmZjQscXGaDQeZgvr3DJO5Nva8Uw==
2279+
dependencies:
2280+
"@babel/runtime" "^7.5.5"
2281+
classnames "^2.2.6"
2282+
lodash.throttle "^4.1.1"
2283+
react-cornerstone-viewport "0.1.30"
2284+
22752285
"@ohif/i18n@^0.2.3":
22762286
version "0.2.5"
22772287
resolved "https://registry.yarnpkg.com/@ohif/i18n/-/i18n-0.2.5.tgz#2b92a78109823ce0f50dd590201d980a5fd64974"

0 commit comments

Comments
 (0)