275 lines
7.5 KiB
JavaScript
275 lines
7.5 KiB
JavaScript
import {
|
|
vtkInteractorStyleMPRCrosshairs,
|
|
vtkInteractorStyleMPRSlice,
|
|
vtkInteractorStyleMPRWindowLevel
|
|
} from 'react-vtkjs-viewport';
|
|
|
|
import setMPRLayout from './utils/setMPRLayout.js';
|
|
import setViewportToVTK from './utils/setViewportToVTK.js';
|
|
import vtkMath from 'vtk.js/Sources/Common/Core/Math';
|
|
import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder';
|
|
|
|
// TODO: Put this somewhere else
|
|
let apis = {};
|
|
|
|
function getCrosshairCallbackForIndex(index) {
|
|
return worldPos => {
|
|
// Set camera focal point to world coordinate for linked views
|
|
apis.forEach((api, viewportIndex) => {
|
|
if (viewportIndex === index) {
|
|
return;
|
|
}
|
|
|
|
// We are basically doing the same as getSlice but with the world coordinate
|
|
// that we want to jump to instead of the camera focal point.
|
|
// I would rather do the camera adjustment directly but I keep
|
|
// doing it wrong and so this is good enough for now.
|
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
|
const istyle = renderWindow.getInteractor().getInteractorStyle();
|
|
const sliceNormal = istyle.getSliceNormal();
|
|
const transform = vtkMatrixBuilder
|
|
.buildFromDegree()
|
|
.identity()
|
|
.rotateFromDirections(sliceNormal, [1, 0, 0]);
|
|
|
|
const mutatedWorldPos = worldPos.slice();
|
|
transform.apply(mutatedWorldPos);
|
|
const slice = mutatedWorldPos[0];
|
|
|
|
istyle.setSlice(slice);
|
|
|
|
renderWindow.render();
|
|
});
|
|
};
|
|
}
|
|
|
|
async function _getActiveViewportVTKApi(viewports) {
|
|
const { layout, viewportSpecificData, activeViewportIndex } = viewports;
|
|
|
|
const currentData = layout.viewports[activeViewportIndex];
|
|
if (currentData && currentData.plugin === 'vtk') {
|
|
// TODO: I was storing/pulling this from Redux but ran into weird issues
|
|
if (apis[activeViewportIndex]) {
|
|
return apis[activeViewportIndex];
|
|
}
|
|
}
|
|
|
|
const displaySet = viewportSpecificData[activeViewportIndex];
|
|
|
|
let api;
|
|
if (!api) {
|
|
try {
|
|
api = await setViewportToVTK(
|
|
displaySet,
|
|
activeViewportIndex,
|
|
layout,
|
|
viewportSpecificData
|
|
);
|
|
} catch (error) {
|
|
throw new Error(error);
|
|
}
|
|
}
|
|
|
|
return api;
|
|
}
|
|
|
|
function _setView(api, sliceNormal, viewUp) {
|
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
|
const renderer = api.genericRenderWindow.getRenderer();
|
|
const camera = renderer.getActiveCamera();
|
|
const istyle = renderWindow.getInteractor().getInteractorStyle();
|
|
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();
|
|
|
|
let currentNormal;
|
|
if (istyle.getSliceNormal) {
|
|
currentNormal = istyle.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);
|
|
|
|
renderWindow.getInteractor().setInteractorStyle(istyle);
|
|
|
|
// TODO: Not sure why this is required the second time this function is called
|
|
istyle.setInteractor(renderWindow.getInteractor());
|
|
|
|
if (istyle.getVolumeMapper() !== api.volumes[0]) {
|
|
if (currentNormal) {
|
|
istyle.setSliceNormal(currentNormal);
|
|
}
|
|
istyle.setVolumeMapper(api.volumes[0]);
|
|
}
|
|
}
|
|
|
|
const actions = {
|
|
axial: async ({ viewports }) => {
|
|
const api = await _getActiveViewportVTKApi(viewports);
|
|
|
|
apis[viewports.activeViewportIndex] = api;
|
|
|
|
_setView(api, [0, 0, 1], [0, -1, 0]);
|
|
},
|
|
sagittal: async ({ viewports }) => {
|
|
const api = await _getActiveViewportVTKApi(viewports);
|
|
|
|
apis[viewports.activeViewportIndex] = api;
|
|
|
|
_setView(api, [1, 0, 0], [0, 0, 1]);
|
|
},
|
|
coronal: async ({ viewports }) => {
|
|
const api = await _getActiveViewportVTKApi(viewports);
|
|
|
|
apis[viewports.activeViewportIndex] = api;
|
|
|
|
_setView(api, [0, 1, 0], [0, 0, 1]);
|
|
},
|
|
enableRotateTool: async ({ viewports }) => {
|
|
apis.forEach(api => {
|
|
const istyle = vtkInteractorStyleMPRSlice.newInstance();
|
|
|
|
switchMPRInteractors(api, istyle);
|
|
});
|
|
},
|
|
enableCrosshairsTool: async ({ viewports }) => {
|
|
apis.forEach((api, index) => {
|
|
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
|
|
|
|
switchMPRInteractors(api, istyle);
|
|
|
|
istyle.setCallback(getCrosshairCallbackForIndex(index));
|
|
});
|
|
},
|
|
enableLevelTool: async ({ viewports }) => {
|
|
apis.forEach(api => {
|
|
const istyle = vtkInteractorStyleMPRWindowLevel.newInstance();
|
|
|
|
switchMPRInteractors(api, istyle);
|
|
});
|
|
},
|
|
mpr2d: async ({ viewports }) => {
|
|
const displaySet =
|
|
viewports.viewportSpecificData[viewports.activeViewportIndex];
|
|
|
|
let apiByViewport;
|
|
try {
|
|
apiByViewport = await setMPRLayout(displaySet);
|
|
} catch (error) {
|
|
throw new Error(error);
|
|
}
|
|
|
|
apis = apiByViewport;
|
|
|
|
/*const rgbTransferFunction = apiByViewport[0].volumes[0]
|
|
.getProperty()
|
|
.getRGBTransferFunction(0);
|
|
rgbTransferFunction.onModified(() => {
|
|
apiByViewport.forEach(a => {
|
|
const renderWindow = a.genericRenderWindow.getRenderWindow();
|
|
|
|
renderWindow.render();
|
|
});
|
|
});*/
|
|
|
|
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);
|
|
|
|
istyle.setVolumeMapper(api.volumes[0]);
|
|
istyle.setCallback(getCrosshairCallbackForIndex(index));
|
|
|
|
switch (index) {
|
|
default:
|
|
case 0:
|
|
//Axial
|
|
istyle.setSliceNormal(0, 0, 1);
|
|
camera.setViewUp(0, -1, 0);
|
|
|
|
break;
|
|
case 1:
|
|
// sagittal
|
|
istyle.setSliceNormal(1, 0, 0);
|
|
camera.setViewUp(0, 0, 1);
|
|
break;
|
|
case 2:
|
|
// Coronal
|
|
istyle.setSliceNormal(0, 1, 0);
|
|
camera.setViewUp(0, 0, 1);
|
|
break;
|
|
}
|
|
|
|
renderWindow.render();
|
|
});
|
|
}
|
|
};
|
|
|
|
const definitions = {
|
|
axial: {
|
|
commandFn: actions.axial,
|
|
storeContexts: ['viewports'],
|
|
options: {}
|
|
},
|
|
coronal: {
|
|
commandFn: actions.coronal,
|
|
storeContexts: ['viewports'],
|
|
options: {}
|
|
},
|
|
sagittal: {
|
|
commandFn: actions.sagittal,
|
|
storeContexts: ['viewports'],
|
|
options: {}
|
|
},
|
|
enableRotateTool: {
|
|
commandFn: actions.enableRotateTool,
|
|
storeContexts: ['viewports'],
|
|
options: {}
|
|
},
|
|
enableCrosshairsTool: {
|
|
commandFn: actions.enableCrosshairsTool,
|
|
storeContexts: ['viewports'],
|
|
options: {}
|
|
},
|
|
enableLevelTool: {
|
|
commandFn: actions.enableLevelTool,
|
|
storeContexts: ['viewports'],
|
|
options: {}
|
|
},
|
|
mpr2d: {
|
|
commandFn: actions.mpr2d,
|
|
storeContexts: ['viewports'],
|
|
options: {},
|
|
context: 'VIEWER'
|
|
}
|
|
};
|
|
|
|
export default {
|
|
definitions,
|
|
defaultContext: 'ACTIVE_VIEWPORT::VTK'
|
|
};
|