diff --git a/examples/VTKCrosshairsExample.js b/examples/VTKCrosshairsExample.js index 5b8d8881..cbf4c64d 100644 --- a/examples/VTKCrosshairsExample.js +++ b/examples/VTKCrosshairsExample.js @@ -2,46 +2,99 @@ import React from 'react'; import { Component } from 'react'; import { View2D, + getImageData, + loadImageData, vtkInteractorStyleMPRCrosshairs, vtkSVGCrosshairsWidget, } from '@vtk-viewport'; -import vtkHttpDataSetReader from 'vtk.js/Sources/IO/Core/HttpDataSetReader'; +import { api as dicomwebClientApi } from 'dicomweb-client'; import vtkVolume from 'vtk.js/Sources/Rendering/Core/Volume'; import vtkVolumeMapper from 'vtk.js/Sources/Rendering/Core/VolumeMapper'; +const url = 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/rs'; +const studyInstanceUID = + '1.3.6.1.4.1.14519.5.2.1.2744.7002.373729467545468642229382466905'; +const ctSeriesInstanceUID = + '1.3.6.1.4.1.14519.5.2.1.2744.7002.182837959725425690842769990419'; +const searchInstanceOptions = { + studyInstanceUID, +}; + +function loadDataset(imageIds, displaySetInstanceUid) { + const imageDataObject = getImageData(imageIds, displaySetInstanceUid); + + loadImageData(imageDataObject); + return imageDataObject; +} + +function createStudyImageIds(baseUrl, studySearchOptions) { + const SOP_INSTANCE_UID = '00080018'; + const SERIES_INSTANCE_UID = '0020000E'; + + const client = new dicomwebClientApi.DICOMwebClient({ url }); + + return new Promise((resolve, reject) => { + client.retrieveStudyMetadata(studySearchOptions).then(instances => { + const imageIds = instances.map(metaData => { + const imageId = + `wadors:` + + baseUrl + + '/studies/' + + studyInstanceUID + + '/series/' + + metaData[SERIES_INSTANCE_UID].Value[0] + + '/instances/' + + metaData[SOP_INSTANCE_UID].Value[0] + + '/frames/1'; + + cornerstoneWADOImageLoader.wadors.metaDataManager.add( + imageId, + metaData + ); + + return imageId; + }); + + resolve(imageIds); + }, reject); + }); +} + class VTKCrosshairsExample extends Component { state = { volumes: [], }; - componentDidMount() { + async componentDidMount() { this.apis = []; - const reader = vtkHttpDataSetReader.newInstance({ - fetchGzip: true, - }); - const volumeActor = vtkVolume.newInstance(); - const volumeMapper = vtkVolumeMapper.newInstance(); + const imageIds = await createStudyImageIds(url, searchInstanceOptions); + + let ctImageIds = imageIds.filter(imageId => + imageId.includes(ctSeriesInstanceUID) + ); + + const ctImageDataObject = loadDataset(ctImageIds, 'ctDisplaySet'); - volumeActor.setMapper(volumeMapper); + Promise.all(ctImageDataObject.insertPixelDataPromises).then(() => { + const ctImageData = ctImageDataObject.vtkImageData; - reader.setUrl('/vmhead2-large.vti', { loadData: true }).then(() => { - const data = reader.getOutputData(); - const range = data + const range = ctImageData .getPointData() .getScalars() .getRange(); - const rgbTransferFunction = volumeActor - .getProperty() - .getRGBTransferFunction(0); + const mapper = vtkVolumeMapper.newInstance(); + const ctVol = vtkVolume.newInstance(); + const rgbTransferFunction = ctVol.getProperty().getRGBTransferFunction(0); + mapper.setInputData(ctImageData); + mapper.setMaximumSamplesPerRay(2000); rgbTransferFunction.setRange(range[0], range[1]); - - volumeMapper.setInputData(data); + ctVol.setMapper(mapper); this.setState({ - volumes: [volumeActor], + volumes: [ctVol], }); }); } @@ -53,6 +106,7 @@ class VTKCrosshairsExample extends Component { const apis = this.apis; const renderWindow = api.genericRenderWindow.getRenderWindow(); + // Add svg widget api.addSVGWidget( vtkSVGCrosshairsWidget.newInstance(), 'crosshairsWidget' @@ -60,15 +114,37 @@ class VTKCrosshairsExample extends Component { const istyle = vtkInteractorStyleMPRCrosshairs.newInstance(); + // add istyle api.setInteractorStyle({ istyle, configuration: { apis, apiIndex: viewportIndex }, }); + // set blend mode to MIP. + const mapper = api.volumes[0].getMapper(); + if (mapper.setBlendModeToMaximumIntensity) { + mapper.setBlendModeToMaximumIntensity(); + } + + api.setSlabThickness(0.1); + renderWindow.render(); }; }; + handleSlabThicknessChange(evt) { + const value = evt.target.value; + const valueInMM = value / 10; + const apis = this.apis; + + apis.forEach(api => { + const renderWindow = api.genericRenderWindow.getRenderWindow(); + + api.setSlabThickness(valueInMM); + renderWindow.render(); + }); + } + render() { if (!this.state.volumes || !this.state.volumes.length) { return

Loading...

; @@ -77,34 +153,40 @@ class VTKCrosshairsExample extends Component { return ( <>
-
+

This example demonstrates how to use the Crosshairs manipulator.

+ +
-
+
-
+
-
- -
-
+
diff --git a/package.json b/package.json index c0e79330..fcdb2eca 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "peerDependencies": { "react": "^16.8.6", "react-dom": "^16.8.6", - "vtk.js": "^11.7.1" + "vtk.js": "^11.7.2" }, "dependencies": { "date-fns": "^2.2.1", @@ -81,7 +81,7 @@ "style-loader": "^0.23.1", "stylelint": "^10.1.0", "stylelint-config-recommended": "^2.2.0", - "vtk.js": "^11.7.1", + "vtk.js": "^11.7.2", "webpack": "4.34.0", "webpack-cli": "^3.3.4", "webpack-dev-server": "^3.8.0", diff --git a/yarn.lock b/yarn.lock index ecd3338b..e1c16feb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13602,10 +13602,10 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.0.tgz#bd76d6a23323e2ca8ffa12028dc04559c75f9019" integrity sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw== -vtk.js@^11.7.1: - version "11.7.1" - resolved "https://registry.yarnpkg.com/vtk.js/-/vtk.js-11.7.1.tgz#e9527c75d84e292c5d25fc4cacf62d42c777930e" - integrity sha512-r6KTDYRF+XBq8gvoi27HJs+1DN4E39FX1k9i15el6JXRuZQebnBjWZ0W8XlfM8Nc6AAAax6DdauBFZdhyG0s/Q== +vtk.js@^11.7.2: + version "11.7.2" + resolved "https://registry.yarnpkg.com/vtk.js/-/vtk.js-11.7.2.tgz#451031a4a03756cfdf44c05490c8c28dee80dd3f" + integrity sha512-ALAJP0gj5kSTOsf/YSicp2wy2YrbCns4vuUQVVKh/vGdlO3npRPt8gxmJLc7QLB2Gwcwl2DKkngu1/q1OC001g== dependencies: blueimp-md5 "2.10.0" commander "2.11.0"