@ohif/extension-vtk: Add basic MIP and Slab Thickness controls. UI to be updated later (#685)
This commit is contained in:
parent
2fd60f3109
commit
d8b4a4d4c0
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-vtk",
|
"name": "@ohif/extension-vtk",
|
||||||
"version": "0.1.4",
|
"version": "0.1.5",
|
||||||
"description": "OHIF extension for VTK.js",
|
"description": "OHIF extension for VTK.js",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -39,7 +39,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.4.5",
|
"@babel/runtime": "^7.4.5",
|
||||||
"react-vtkjs-viewport": "0.0.10",
|
"react-vtkjs-viewport": "0.0.11",
|
||||||
"vtk.js": "^8.13.0"
|
"vtk.js": "^8.13.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
vtkInteractorStyleMPRCrosshairs,
|
vtkInteractorStyleMPRCrosshairs,
|
||||||
vtkInteractorStyleMPRSlice,
|
|
||||||
vtkInteractorStyleMPRWindowLevel,
|
vtkInteractorStyleMPRWindowLevel,
|
||||||
|
vtkInteractorStyleMPRSlice,
|
||||||
vtkSVGCrosshairsWidget,
|
vtkSVGCrosshairsWidget,
|
||||||
vtkSVGWidgetManager,
|
vtkSVGWidgetManager,
|
||||||
} from 'react-vtkjs-viewport';
|
} from 'react-vtkjs-viewport';
|
||||||
@ -11,9 +11,13 @@ import setViewportToVTK from './utils/setViewportToVTK.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';
|
||||||
|
import Constants from 'vtk.js/Sources/Rendering/Core/VolumeMapper/Constants.js';
|
||||||
|
|
||||||
|
const { BlendMode } = Constants;
|
||||||
|
|
||||||
// TODO: Put this somewhere else
|
// TODO: Put this somewhere else
|
||||||
let apis = {};
|
let apis = {};
|
||||||
|
let currentSlabThickness = 0.1;
|
||||||
|
|
||||||
function getCrosshairCallbackForIndex(index) {
|
function getCrosshairCallbackForIndex(index) {
|
||||||
return ({ worldPos }) => {
|
return ({ worldPos }) => {
|
||||||
@ -96,26 +100,24 @@ function _setView(api, sliceNormal, viewUp) {
|
|||||||
istyle.setSliceNormal(...sliceNormal);
|
istyle.setSliceNormal(...sliceNormal);
|
||||||
camera.setViewUp(...viewUp);
|
camera.setViewUp(...viewUp);
|
||||||
|
|
||||||
api.volumes[0].getMapper().setSampleDistance(5.0);
|
|
||||||
api.volumes[0].getMapper().setMaximumSamplesPerRay(2000);
|
|
||||||
|
|
||||||
renderWindow.render();
|
renderWindow.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
function switchMPRInteractors(api, istyle) {
|
function switchMPRInteractors(api, istyle) {
|
||||||
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
||||||
|
const renderer = api.genericRenderWindow.getRenderer();
|
||||||
|
const camera = renderer.getActiveCamera();
|
||||||
|
const currentIStyle = renderWindow.getInteractor().getInteractorStyle();
|
||||||
|
|
||||||
let currentNormal;
|
let currentNormal;
|
||||||
if (istyle.getSliceNormal) {
|
if (currentIStyle.getSliceNormal && istyle.getSliceNormal) {
|
||||||
currentNormal = istyle.getSliceNormal();
|
currentNormal = currentIStyle.getSliceNormal();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This is a hacky workaround because disabling the vtkInteractorStyleMPRSlice is currently
|
let currentSlabThickness;
|
||||||
// broken. The camera.onModified is never removed. (https://github.com/Kitware/vtk-js/issues/1110)
|
if (currentIStyle.getSlabThickness && istyle.getSlabThickness) {
|
||||||
renderWindow
|
currentSlabThickness = currentIStyle.getSlabThickness();
|
||||||
.getInteractor()
|
}
|
||||||
.getInteractorStyle()
|
|
||||||
.setInteractor(null);
|
|
||||||
|
|
||||||
renderWindow.getInteractor().setInteractorStyle(istyle);
|
renderWindow.getInteractor().setInteractorStyle(istyle);
|
||||||
|
|
||||||
@ -126,6 +128,11 @@ function switchMPRInteractors(api, istyle) {
|
|||||||
if (currentNormal) {
|
if (currentNormal) {
|
||||||
istyle.setSliceNormal(currentNormal);
|
istyle.setSliceNormal(currentNormal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentSlabThickness) {
|
||||||
|
istyle.setSlabThickness(currentSlabThickness);
|
||||||
|
}
|
||||||
|
|
||||||
istyle.setVolumeMapper(api.volumes[0]);
|
istyle.setVolumeMapper(api.volumes[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,14 +159,14 @@ const actions = {
|
|||||||
|
|
||||||
_setView(api, [0, 1, 0], [0, 0, 1]);
|
_setView(api, [0, 1, 0], [0, 0, 1]);
|
||||||
},
|
},
|
||||||
enableRotateTool: async ({ viewports }) => {
|
enableRotateTool: () => {
|
||||||
apis.forEach(api => {
|
apis.forEach(api => {
|
||||||
const istyle = vtkInteractorStyleMPRSlice.newInstance();
|
const istyle = vtkInteractorStyleMPRSlice.newInstance();
|
||||||
|
|
||||||
switchMPRInteractors(api, istyle);
|
switchMPRInteractors(api, istyle);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
enableCrosshairsTool: async ({ viewports }) => {
|
enableCrosshairsTool: () => {
|
||||||
apis.forEach((api, index) => {
|
apis.forEach((api, index) => {
|
||||||
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
|
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
|
||||||
|
|
||||||
@ -168,13 +175,61 @@ const actions = {
|
|||||||
istyle.setCallback(getCrosshairCallbackForIndex(index));
|
istyle.setCallback(getCrosshairCallbackForIndex(index));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
enableLevelTool: async ({ viewports }) => {
|
enableLevelTool: () => {
|
||||||
apis.forEach(api => {
|
apis.forEach(api => {
|
||||||
const istyle = vtkInteractorStyleMPRWindowLevel.newInstance();
|
const istyle = vtkInteractorStyleMPRWindowLevel.newInstance();
|
||||||
|
|
||||||
switchMPRInteractors(api, istyle);
|
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 }) => {
|
mpr2d: async ({ viewports }) => {
|
||||||
const displaySet =
|
const displaySet =
|
||||||
viewports.viewportSpecificData[viewports.activeViewportIndex];
|
viewports.viewportSpecificData[viewports.activeViewportIndex];
|
||||||
@ -199,20 +254,11 @@ const actions = {
|
|||||||
});
|
});
|
||||||
});*/
|
});*/
|
||||||
|
|
||||||
apis[0].volumes[0].getMapper().setSampleDistance(1.5);
|
|
||||||
|
|
||||||
apiByViewport.forEach((api, index) => {
|
apiByViewport.forEach((api, index) => {
|
||||||
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
||||||
const renderer = api.genericRenderWindow.getRenderer();
|
const renderer = api.genericRenderWindow.getRenderer();
|
||||||
const camera = renderer.getActiveCamera();
|
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();
|
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
|
||||||
renderWindow.getInteractor().setInteractorStyle(istyle);
|
renderWindow.getInteractor().setInteractorStyle(istyle);
|
||||||
|
|
||||||
@ -258,6 +304,8 @@ const actions = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.vtkActions = actions;
|
||||||
|
|
||||||
const definitions = {
|
const definitions = {
|
||||||
axial: {
|
axial: {
|
||||||
commandFn: actions.axial,
|
commandFn: actions.axial,
|
||||||
@ -276,19 +324,59 @@ const definitions = {
|
|||||||
},
|
},
|
||||||
enableRotateTool: {
|
enableRotateTool: {
|
||||||
commandFn: actions.enableRotateTool,
|
commandFn: actions.enableRotateTool,
|
||||||
storeContexts: ['viewports'],
|
storeContexts: [],
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
enableCrosshairsTool: {
|
enableCrosshairsTool: {
|
||||||
commandFn: actions.enableCrosshairsTool,
|
commandFn: actions.enableCrosshairsTool,
|
||||||
storeContexts: ['viewports'],
|
storeContexts: [],
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
enableLevelTool: {
|
enableLevelTool: {
|
||||||
commandFn: actions.enableLevelTool,
|
commandFn: actions.enableLevelTool,
|
||||||
storeContexts: ['viewports'],
|
storeContexts: [],
|
||||||
options: {},
|
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: {
|
mpr2d: {
|
||||||
commandFn: actions.mpr2d,
|
commandFn: actions.mpr2d,
|
||||||
storeContexts: ['viewports'],
|
storeContexts: ['viewports'],
|
||||||
|
|||||||
@ -31,6 +31,43 @@ const definitions = [
|
|||||||
commandName: 'enableRotateTool',
|
commandName: 'enableRotateTool',
|
||||||
commandOptions: {},
|
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 {
|
export default {
|
||||||
|
|||||||
@ -5519,10 +5519,10 @@ react-viewerbase@^0.15.1:
|
|||||||
react-i18next "^10.11.0"
|
react-i18next "^10.11.0"
|
||||||
react-with-direction "1.3.0"
|
react-with-direction "1.3.0"
|
||||||
|
|
||||||
react-vtkjs-viewport@0.0.9:
|
react-vtkjs-viewport@0.0.11:
|
||||||
version "0.0.9"
|
version "0.0.11"
|
||||||
resolved "https://registry.yarnpkg.com/react-vtkjs-viewport/-/react-vtkjs-viewport-0.0.9.tgz#da79aa66f52dcd731bcc5abd2587cbb120a25331"
|
resolved "https://registry.yarnpkg.com/react-vtkjs-viewport/-/react-vtkjs-viewport-0.0.11.tgz#cedd993ca226502bf0417a3748a5b2f806522519"
|
||||||
integrity sha512-VwRCY2DPxWMhix4v3PAtdENp3U1p+Z9nKf1Ch5+KWRKF6fWeA+B2glYKMYNpPnP4bULzVz9iwV+Zeojt3gEibA==
|
integrity sha512-ttWw7bU/lL/nJuaaKdtAc8448vMJmlRQzpkpuP/J+3lTzIQbQPCPU40LqgONjr7LvL5++61N2GJ8m+PRgHyfxQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
moment "^2.24.0"
|
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"
|
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
|
||||||
integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=
|
integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=
|
||||||
|
|
||||||
vtk.js@^8.11.0:
|
vtk.js@^8.13.0:
|
||||||
version "8.11.1"
|
version "8.13.0"
|
||||||
resolved "https://registry.yarnpkg.com/vtk.js/-/vtk.js-8.11.1.tgz#cdf69705a72a2a3ce76d7442d1b3903e3de770d4"
|
resolved "https://registry.yarnpkg.com/vtk.js/-/vtk.js-8.13.0.tgz#d22bbe3403d86f979703506f9be83ab8e5099248"
|
||||||
integrity sha512-Y5ZCFtoMOuuJt44fyj91Gqeg9ZeCQovyoJlEHG1oUHLPehQTvq0oW09WqBcxVSdiv92x3aXYWtgZjm/s4EWw7Q==
|
integrity sha512-uoq2U0Yu2XjuKeeduuGRXlphHR6cdIvtZ5useNMJ8FobK9dpeFTT4ncoyPXx77KvwOMSueT8W+ngm/e9+FGZtA==
|
||||||
dependencies:
|
dependencies:
|
||||||
base64-js "1.2.1"
|
base64-js "1.2.1"
|
||||||
blueimp-md5 "2.10.0"
|
blueimp-md5 "2.10.0"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user