diff --git a/extensions/ohif-vtk-extension/package.json b/extensions/ohif-vtk-extension/package.json index 1b9105bcf..f16e7db05 100644 --- a/extensions/ohif-vtk-extension/package.json +++ b/extensions/ohif-vtk-extension/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/extension-vtk", - "version": "0.1.4", + "version": "0.1.5", "description": "OHIF extension for VTK.js", "author": "OHIF", "license": "MIT", @@ -39,7 +39,7 @@ }, "dependencies": { "@babel/runtime": "^7.4.5", - "react-vtkjs-viewport": "0.0.10", + "react-vtkjs-viewport": "0.0.11", "vtk.js": "^8.13.0" }, "devDependencies": { diff --git a/extensions/ohif-vtk-extension/src/commandsModule.js b/extensions/ohif-vtk-extension/src/commandsModule.js index b8cc692c0..677a50fc1 100644 --- a/extensions/ohif-vtk-extension/src/commandsModule.js +++ b/extensions/ohif-vtk-extension/src/commandsModule.js @@ -1,7 +1,7 @@ import { vtkInteractorStyleMPRCrosshairs, - vtkInteractorStyleMPRSlice, vtkInteractorStyleMPRWindowLevel, + vtkInteractorStyleMPRSlice, vtkSVGCrosshairsWidget, vtkSVGWidgetManager, } from 'react-vtkjs-viewport'; @@ -11,9 +11,13 @@ import setViewportToVTK from './utils/setViewportToVTK.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'; +import Constants from 'vtk.js/Sources/Rendering/Core/VolumeMapper/Constants.js'; + +const { BlendMode } = Constants; // TODO: Put this somewhere else let apis = {}; +let currentSlabThickness = 0.1; function getCrosshairCallbackForIndex(index) { return ({ worldPos }) => { @@ -96,26 +100,24 @@ function _setView(api, sliceNormal, viewUp) { 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(); + const renderer = api.genericRenderWindow.getRenderer(); + const camera = renderer.getActiveCamera(); + const currentIStyle = renderWindow.getInteractor().getInteractorStyle(); let currentNormal; - if (istyle.getSliceNormal) { - currentNormal = istyle.getSliceNormal(); + if (currentIStyle.getSliceNormal && istyle.getSliceNormal) { + currentNormal = currentIStyle.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); + let currentSlabThickness; + if (currentIStyle.getSlabThickness && istyle.getSlabThickness) { + currentSlabThickness = currentIStyle.getSlabThickness(); + } renderWindow.getInteractor().setInteractorStyle(istyle); @@ -126,6 +128,11 @@ function switchMPRInteractors(api, istyle) { if (currentNormal) { istyle.setSliceNormal(currentNormal); } + + if (currentSlabThickness) { + istyle.setSlabThickness(currentSlabThickness); + } + istyle.setVolumeMapper(api.volumes[0]); } } @@ -152,14 +159,14 @@ const actions = { _setView(api, [0, 1, 0], [0, 0, 1]); }, - enableRotateTool: async ({ viewports }) => { + enableRotateTool: () => { apis.forEach(api => { const istyle = vtkInteractorStyleMPRSlice.newInstance(); switchMPRInteractors(api, istyle); }); }, - enableCrosshairsTool: async ({ viewports }) => { + enableCrosshairsTool: () => { apis.forEach((api, index) => { const istyle = vtkInteractorStyleMPRCrosshairs.newInstance(); @@ -168,13 +175,61 @@ const actions = { istyle.setCallback(getCrosshairCallbackForIndex(index)); }); }, - enableLevelTool: async ({ viewports }) => { + enableLevelTool: () => { apis.forEach(api => { const istyle = vtkInteractorStyleMPRWindowLevel.newInstance(); switchMPRInteractors(api, istyle); }); }, + setSlabThickness: slabThickness => { + currentSlabThickness = slabThickness; + + apis.forEach(api => { + const renderWindow = api.genericRenderWindow.getRenderWindow(); + const istyle = renderWindow.getInteractor().getInteractorStyle(); + + if (istyle.setSlabThickness) { + istyle.setSlabThickness(currentSlabThickness); + + // TODO: Do this inside the interactors in a setSlabThickness function instead + const renderer = api.genericRenderWindow.getRenderer(); + const camera = renderer.getActiveCamera(); + const dist = camera.getDistance(); + const near = dist - currentSlabThickness / 2; + const far = dist + currentSlabThickness / 2; + + camera.setClippingRange(near, far); + } + + renderWindow.render(); + }); + }, + changeSlabThickness: ({ change }) => { + currentSlabThickness += change; + currentSlabThickness = Math.max(currentSlabThickness, 0.1); + + apis.forEach(api => { + const renderWindow = api.genericRenderWindow.getRenderWindow(); + const istyle = renderWindow.getInteractor().getInteractorStyle(); + + if (istyle.setSlabThickness) { + istyle.setSlabThickness(currentSlabThickness); + } + + renderWindow.render(); + }); + }, + setBlendMode: ({ blendMode }) => { + apis.forEach(api => { + const renderWindow = api.genericRenderWindow.getRenderWindow(); + const istyle = renderWindow.getInteractor().getInteractorStyle(); + + api.volumes[0].getMapper().setBlendMode(blendMode); + + renderWindow.render(); + }); + }, mpr2d: async ({ viewports }) => { const displaySet = viewports.viewportSpecificData[viewports.activeViewportIndex]; @@ -199,20 +254,11 @@ const actions = { }); });*/ - 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); @@ -258,6 +304,8 @@ const actions = { }, }; +window.vtkActions = actions; + const definitions = { axial: { commandFn: actions.axial, @@ -276,19 +324,59 @@ const definitions = { }, enableRotateTool: { commandFn: actions.enableRotateTool, - storeContexts: ['viewports'], + storeContexts: [], options: {}, }, enableCrosshairsTool: { commandFn: actions.enableCrosshairsTool, - storeContexts: ['viewports'], + storeContexts: [], options: {}, }, enableLevelTool: { commandFn: actions.enableLevelTool, - storeContexts: ['viewports'], + storeContexts: [], options: {}, }, + setBlendModeToComposite: { + commandFn: actions.setBlendMode, + storeContexts: [], + options: { blendMode: BlendMode.COMPOSITE_BLEND }, + }, + setBlendModeToMaximumIntensity: { + commandFn: actions.setBlendMode, + storeContexts: [], + options: { blendMode: BlendMode.MAXIMUM_INTENSITY_BLEND }, + }, + setBlendModeToMinimumIntensity: { + commandFn: actions.setBlendMode, + storeContexts: [], + options: { blendMode: BlendMode.MINIMUM_INTENSITY_BLEND }, + }, + setBlendModeToAverageIntensity: { + commandFn: actions.setBlendMode, + storeContexts: [], + options: { blendMode: BlendMode.AVERAGE_INTENSITY_BLEND }, + }, + setSlabThickness: { + // TODO: How do we pass in a function argument? + commandFn: actions.setSlabThickness, + storeContexts: [], + options: {}, + }, + increaseSlabThickness: { + commandFn: actions.changeSlabThickness, + storeContexts: [], + options: { + change: 3, + }, + }, + decreaseSlabThickness: { + commandFn: actions.changeSlabThickness, + storeContexts: [], + options: { + change: -3, + }, + }, mpr2d: { commandFn: actions.mpr2d, storeContexts: ['viewports'], diff --git a/extensions/ohif-vtk-extension/src/toolbarModule.js b/extensions/ohif-vtk-extension/src/toolbarModule.js index fed4f54db..9f2832047 100644 --- a/extensions/ohif-vtk-extension/src/toolbarModule.js +++ b/extensions/ohif-vtk-extension/src/toolbarModule.js @@ -31,6 +31,43 @@ const definitions = [ commandName: 'enableRotateTool', commandOptions: {}, }, + { + id: 'setBlendModeToComposite', + label: 'Disable MIP', + icon: 'times', + // + type: TOOLBAR_BUTTON_TYPES.COMMAND, + commandName: 'setBlendModeToComposite', + commandOptions: {}, + }, + { + id: 'setBlendModeToMaximumIntensity', + label: 'Enable MIP', + icon: 'soft-tissue', + // + type: TOOLBAR_BUTTON_TYPES.COMMAND, + commandName: 'setBlendModeToMaximumIntensity', + commandOptions: {}, + }, + + { + id: 'increaseSlabThickness', + label: 'Increase Slab Thickness', + icon: 'caret-up', + // + type: TOOLBAR_BUTTON_TYPES.COMMAND, + commandName: 'increaseSlabThickness', + commandOptions: {}, + }, + { + id: 'decreaseSlabThickness', + label: 'Decrease Slab Thickness', + icon: 'caret-down', + // + type: TOOLBAR_BUTTON_TYPES.COMMAND, + commandName: 'decreaseSlabThickness', + commandOptions: {}, + }, ]; export default { diff --git a/extensions/ohif-vtk-extension/yarn.lock b/extensions/ohif-vtk-extension/yarn.lock index 59c44c403..82cfa3811 100644 --- a/extensions/ohif-vtk-extension/yarn.lock +++ b/extensions/ohif-vtk-extension/yarn.lock @@ -5519,10 +5519,10 @@ react-viewerbase@^0.15.1: react-i18next "^10.11.0" react-with-direction "1.3.0" -react-vtkjs-viewport@0.0.9: - version "0.0.9" - resolved "https://registry.yarnpkg.com/react-vtkjs-viewport/-/react-vtkjs-viewport-0.0.9.tgz#da79aa66f52dcd731bcc5abd2587cbb120a25331" - integrity sha512-VwRCY2DPxWMhix4v3PAtdENp3U1p+Z9nKf1Ch5+KWRKF6fWeA+B2glYKMYNpPnP4bULzVz9iwV+Zeojt3gEibA== +react-vtkjs-viewport@0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/react-vtkjs-viewport/-/react-vtkjs-viewport-0.0.11.tgz#cedd993ca226502bf0417a3748a5b2f806522519" + integrity sha512-ttWw7bU/lL/nJuaaKdtAc8448vMJmlRQzpkpuP/J+3lTzIQbQPCPU40LqgONjr7LvL5++61N2GJ8m+PRgHyfxQ== dependencies: moment "^2.24.0" @@ -6974,10 +6974,10 @@ void-elements@^2.0.1: resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= -vtk.js@^8.11.0: - version "8.11.1" - resolved "https://registry.yarnpkg.com/vtk.js/-/vtk.js-8.11.1.tgz#cdf69705a72a2a3ce76d7442d1b3903e3de770d4" - integrity sha512-Y5ZCFtoMOuuJt44fyj91Gqeg9ZeCQovyoJlEHG1oUHLPehQTvq0oW09WqBcxVSdiv92x3aXYWtgZjm/s4EWw7Q== +vtk.js@^8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/vtk.js/-/vtk.js-8.13.0.tgz#d22bbe3403d86f979703506f9be83ab8e5099248" + integrity sha512-uoq2U0Yu2XjuKeeduuGRXlphHR6cdIvtZ5useNMJ8FobK9dpeFTT4ncoyPXx77KvwOMSueT8W+ngm/e9+FGZtA== dependencies: base64-js "1.2.1" blueimp-md5 "2.10.0"