2019-12-02 13:29:04 -03:00
|
|
|
import throttle from 'lodash.throttle';
|
2020-04-09 07:06:41 -04:00
|
|
|
import {
|
|
|
|
|
vtkInteractorStyleMPRWindowLevel,
|
2020-10-13 15:00:01 +02:00
|
|
|
vtkInteractorStyleRotatableMPRCrosshairs,
|
|
|
|
|
vtkSVGRotatableCrosshairsWidget,
|
2020-04-09 07:06:41 -04:00
|
|
|
vtkInteractorStyleMPRRotate,
|
|
|
|
|
} from 'react-vtkjs-viewport';
|
2020-05-06 14:17:44 -03:00
|
|
|
import { getImageData } from 'react-vtkjs-viewport';
|
|
|
|
|
import { vec3 } from 'gl-matrix';
|
2019-06-15 11:51:56 +02:00
|
|
|
import setMPRLayout from './utils/setMPRLayout.js';
|
2019-06-16 23:11:43 -04:00
|
|
|
import setViewportToVTK from './utils/setViewportToVTK.js';
|
2019-07-18 13:39:00 +02:00
|
|
|
import Constants from 'vtk.js/Sources/Rendering/Core/VolumeMapper/Constants.js';
|
2020-05-06 14:17:44 -03:00
|
|
|
import OHIFVTKViewport from './OHIFVTKViewport';
|
2019-07-18 13:39:00 +02:00
|
|
|
|
|
|
|
|
const { BlendMode } = Constants;
|
2019-06-15 11:51:56 +02:00
|
|
|
|
2021-02-25 18:29:28 -03:00
|
|
|
const commandsModule = ({ commandsManager, servicesManager }) => {
|
|
|
|
|
const { UINotificationService, LoggerService } = servicesManager.services;
|
|
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
// TODO: Put this somewhere else
|
|
|
|
|
let apis = {};
|
2020-12-03 14:11:14 -03:00
|
|
|
let defaultVOI;
|
2019-12-02 13:29:04 -03:00
|
|
|
|
|
|
|
|
async function _getActiveViewportVTKApi(viewports) {
|
|
|
|
|
const {
|
|
|
|
|
numRows,
|
|
|
|
|
numColumns,
|
|
|
|
|
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];
|
|
|
|
|
}
|
2019-06-15 11:51:56 +02:00
|
|
|
}
|
|
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
const displaySet = viewportSpecificData[activeViewportIndex];
|
|
|
|
|
let api;
|
|
|
|
|
if (!api) {
|
|
|
|
|
try {
|
|
|
|
|
api = await setViewportToVTK(
|
|
|
|
|
displaySet,
|
|
|
|
|
activeViewportIndex,
|
|
|
|
|
numRows,
|
|
|
|
|
numColumns,
|
|
|
|
|
layout,
|
|
|
|
|
viewportSpecificData
|
|
|
|
|
);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
throw new Error(error);
|
|
|
|
|
}
|
2019-06-15 11:51:56 +02:00
|
|
|
}
|
2019-12-02 13:29:04 -03:00
|
|
|
|
|
|
|
|
return api;
|
2019-06-15 11:51:56 +02:00
|
|
|
}
|
|
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
function _setView(api, sliceNormal, viewUp) {
|
|
|
|
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
|
|
|
|
const istyle = renderWindow.getInteractor().getInteractorStyle();
|
|
|
|
|
istyle.setSliceNormal(...sliceNormal);
|
|
|
|
|
istyle.setViewUp(...viewUp);
|
2019-06-15 11:51:56 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
renderWindow.render();
|
|
|
|
|
}
|
2019-06-15 11:51:56 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
function getVOIFromCornerstoneViewport() {
|
|
|
|
|
const dom = commandsManager.runCommand('getActiveViewportEnabledElement');
|
|
|
|
|
const cornerstoneElement = cornerstone.getEnabledElement(dom);
|
2019-06-15 11:51:56 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
if (cornerstoneElement) {
|
|
|
|
|
const imageId = cornerstoneElement.image.imageId;
|
2019-06-15 11:51:56 +02:00
|
|
|
|
2020-03-09 19:03:23 +00:00
|
|
|
const Modality = cornerstone.metaData.get('Modality', imageId);
|
2019-06-15 11:51:56 +02:00
|
|
|
|
2020-03-09 19:03:23 +00:00
|
|
|
if (Modality !== 'PT') {
|
2019-12-02 13:29:04 -03:00
|
|
|
const { windowWidth, windowCenter } = cornerstoneElement.viewport.voi;
|
2019-06-15 11:51:56 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
return {
|
|
|
|
|
windowWidth,
|
|
|
|
|
windowCenter,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-06-15 11:51:56 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
function setVOI(voi) {
|
|
|
|
|
const { windowWidth, windowCenter } = voi;
|
|
|
|
|
const lower = windowCenter - windowWidth / 2.0;
|
|
|
|
|
const upper = windowCenter + windowWidth / 2.0;
|
2019-06-15 11:51:56 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
const rgbTransferFunction = apis[0].volumes[0]
|
|
|
|
|
.getProperty()
|
|
|
|
|
.getRGBTransferFunction(0);
|
2019-06-15 11:51:56 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
rgbTransferFunction.setRange(lower, upper);
|
2019-06-15 11:51:56 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
apis.forEach(api => {
|
|
|
|
|
api.updateVOI(windowWidth, windowCenter);
|
2019-06-15 11:51:56 +02:00
|
|
|
});
|
2019-12-02 13:29:04 -03:00
|
|
|
}
|
2019-10-23 09:45:26 +02:00
|
|
|
|
2020-05-06 14:17:44 -03:00
|
|
|
const _convertModelToWorldSpace = (position, vtkImageData) => {
|
|
|
|
|
const indexToWorld = vtkImageData.getIndexToWorld();
|
|
|
|
|
const pos = vec3.create();
|
|
|
|
|
|
|
|
|
|
position[0] += 0.5; /* Move to the centre of the voxel. */
|
|
|
|
|
position[1] += 0.5; /* Move to the centre of the voxel. */
|
|
|
|
|
position[2] += 0.5; /* Move to the centre of the voxel. */
|
|
|
|
|
|
|
|
|
|
vec3.set(pos, position[0], position[1], position[2]);
|
|
|
|
|
vec3.transformMat4(pos, pos, indexToWorld);
|
|
|
|
|
|
|
|
|
|
return pos;
|
|
|
|
|
};
|
|
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
const actions = {
|
2020-01-30 15:26:06 -03:00
|
|
|
getVtkApis: ({ index }) => {
|
|
|
|
|
return apis[index];
|
|
|
|
|
},
|
2020-10-13 15:00:01 +02:00
|
|
|
resetMPRView() {
|
2020-12-03 14:11:14 -03:00
|
|
|
// Reset orientation
|
|
|
|
|
apis.forEach(api => api.resetOrientation());
|
|
|
|
|
|
|
|
|
|
// Reset VOI
|
|
|
|
|
if (defaultVOI) setVOI(defaultVOI);
|
2020-10-13 15:00:01 +02:00
|
|
|
|
|
|
|
|
// Reset the crosshairs
|
|
|
|
|
apis[0].svgWidgets.rotatableCrosshairsWidget.resetCrosshairs(apis, 0);
|
|
|
|
|
},
|
2019-12-02 13:29:04 -03:00
|
|
|
axial: async ({ viewports }) => {
|
|
|
|
|
const api = await _getActiveViewportVTKApi(viewports);
|
2019-10-23 09:45:26 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
apis[viewports.activeViewportIndex] = api;
|
2019-10-18 15:53:43 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
_setView(api, [0, 0, 1], [0, -1, 0]);
|
|
|
|
|
},
|
|
|
|
|
sagittal: async ({ viewports }) => {
|
|
|
|
|
const api = await _getActiveViewportVTKApi(viewports);
|
2019-10-23 09:45:26 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
apis[viewports.activeViewportIndex] = api;
|
2019-10-18 15:53:43 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
_setView(api, [1, 0, 0], [0, 0, 1]);
|
|
|
|
|
},
|
|
|
|
|
coronal: async ({ viewports }) => {
|
|
|
|
|
const api = await _getActiveViewportVTKApi(viewports);
|
2019-06-15 11:51:56 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
apis[viewports.activeViewportIndex] = api;
|
2019-07-18 13:39:00 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
_setView(api, [0, 1, 0], [0, 0, 1]);
|
|
|
|
|
},
|
2020-05-06 14:17:44 -03:00
|
|
|
requestNewSegmentation: async ({ viewports }) => {
|
|
|
|
|
const allViewports = Object.values(viewports.viewportSpecificData);
|
|
|
|
|
const promises = allViewports.map(async (viewport, viewportIndex) => {
|
|
|
|
|
let api = apis[viewportIndex];
|
|
|
|
|
|
|
|
|
|
if (!api) {
|
|
|
|
|
api = await _getActiveViewportVTKApi(viewports);
|
|
|
|
|
apis[viewportIndex] = api;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
api.requestNewSegmentation();
|
|
|
|
|
api.updateImage();
|
|
|
|
|
});
|
|
|
|
|
await Promise.all(promises);
|
|
|
|
|
},
|
|
|
|
|
jumpToSlice: async ({
|
|
|
|
|
viewports,
|
|
|
|
|
studies,
|
|
|
|
|
StudyInstanceUID,
|
|
|
|
|
displaySetInstanceUID,
|
|
|
|
|
SOPClassUID,
|
|
|
|
|
SOPInstanceUID,
|
|
|
|
|
segmentNumber,
|
|
|
|
|
frameIndex,
|
|
|
|
|
frame,
|
2021-02-25 18:29:28 -03:00
|
|
|
done = () => {},
|
2020-05-06 14:17:44 -03:00
|
|
|
}) => {
|
|
|
|
|
let api = apis[viewports.activeViewportIndex];
|
|
|
|
|
|
|
|
|
|
if (!api) {
|
|
|
|
|
api = await _getActiveViewportVTKApi(viewports);
|
|
|
|
|
apis[viewports.activeViewportIndex] = api;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const stack = OHIFVTKViewport.getCornerstoneStack(
|
|
|
|
|
studies,
|
|
|
|
|
StudyInstanceUID,
|
|
|
|
|
displaySetInstanceUID,
|
|
|
|
|
SOPClassUID,
|
|
|
|
|
SOPInstanceUID,
|
2020-10-13 15:00:01 +02:00
|
|
|
frameIndex
|
2020-05-06 14:17:44 -03:00
|
|
|
);
|
|
|
|
|
|
2020-10-13 15:00:01 +02:00
|
|
|
const imageDataObject = getImageData(
|
|
|
|
|
stack.imageIds,
|
|
|
|
|
displaySetInstanceUID
|
|
|
|
|
);
|
2020-05-06 14:17:44 -03:00
|
|
|
|
|
|
|
|
let pixelIndex = 0;
|
|
|
|
|
let x = 0;
|
|
|
|
|
let y = 0;
|
|
|
|
|
let count = 0;
|
|
|
|
|
|
|
|
|
|
const rows = imageDataObject.dimensions[1];
|
|
|
|
|
const cols = imageDataObject.dimensions[0];
|
|
|
|
|
|
|
|
|
|
for (let j = 0; j < rows; j++) {
|
|
|
|
|
for (let i = 0; i < cols; i++) {
|
|
|
|
|
// [i, j] =
|
|
|
|
|
const pixel = frame.pixelData[pixelIndex];
|
|
|
|
|
if (pixel === segmentNumber) {
|
|
|
|
|
x += i;
|
|
|
|
|
y += j;
|
|
|
|
|
count++;
|
|
|
|
|
}
|
|
|
|
|
pixelIndex++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
x /= count;
|
|
|
|
|
y /= count;
|
|
|
|
|
|
|
|
|
|
const position = [x, y, frameIndex];
|
2020-10-13 15:00:01 +02:00
|
|
|
const worldPos = _convertModelToWorldSpace(
|
|
|
|
|
position,
|
|
|
|
|
imageDataObject.vtkImageData
|
|
|
|
|
);
|
2020-05-06 14:17:44 -03:00
|
|
|
|
2020-10-13 15:00:01 +02:00
|
|
|
api.svgWidgets.rotatableCrosshairsWidget.moveCrosshairs(worldPos, apis);
|
2020-05-06 14:17:44 -03:00
|
|
|
done();
|
|
|
|
|
},
|
|
|
|
|
setSegmentationConfiguration: async ({
|
|
|
|
|
viewports,
|
|
|
|
|
globalOpacity,
|
|
|
|
|
visible,
|
|
|
|
|
renderOutline,
|
|
|
|
|
outlineThickness,
|
|
|
|
|
}) => {
|
|
|
|
|
const allViewports = Object.values(viewports.viewportSpecificData);
|
|
|
|
|
const promises = allViewports.map(async (viewport, viewportIndex) => {
|
|
|
|
|
let api = apis[viewportIndex];
|
|
|
|
|
|
|
|
|
|
if (!api) {
|
|
|
|
|
api = await _getActiveViewportVTKApi(viewports);
|
|
|
|
|
apis[viewportIndex] = api;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
api.setGlobalOpacity(globalOpacity);
|
|
|
|
|
api.setVisibility(visible);
|
|
|
|
|
api.setOutlineThickness(outlineThickness);
|
|
|
|
|
api.setOutlineRendering(renderOutline);
|
|
|
|
|
api.updateImage();
|
|
|
|
|
});
|
|
|
|
|
await Promise.all(promises);
|
|
|
|
|
},
|
|
|
|
|
setSegmentConfiguration: async ({ viewports, visible, segmentNumber }) => {
|
|
|
|
|
const allViewports = Object.values(viewports.viewportSpecificData);
|
|
|
|
|
const promises = allViewports.map(async (viewport, viewportIndex) => {
|
|
|
|
|
let api = apis[viewportIndex];
|
|
|
|
|
|
|
|
|
|
if (!api) {
|
|
|
|
|
api = await _getActiveViewportVTKApi(viewports);
|
|
|
|
|
apis[viewportIndex] = api;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
api.setSegmentVisibility(segmentNumber, visible);
|
|
|
|
|
api.updateImage();
|
|
|
|
|
});
|
|
|
|
|
await Promise.all(promises);
|
|
|
|
|
},
|
2020-04-09 07:06:41 -04:00
|
|
|
enableRotateTool: () => {
|
2020-10-13 15:00:01 +02:00
|
|
|
apis.forEach((api, apiIndex) => {
|
2020-04-09 07:06:41 -04:00
|
|
|
const istyle = vtkInteractorStyleMPRRotate.newInstance();
|
2019-10-01 05:06:56 -03:00
|
|
|
|
2020-10-15 16:15:28 +02:00
|
|
|
api.setInteractorStyle({
|
|
|
|
|
istyle,
|
|
|
|
|
configuration: { apis, apiIndex, uid: api.uid },
|
|
|
|
|
});
|
2019-12-02 13:29:04 -03:00
|
|
|
});
|
|
|
|
|
},
|
2020-04-09 07:06:41 -04:00
|
|
|
enableCrosshairsTool: () => {
|
2019-12-02 13:29:04 -03:00
|
|
|
apis.forEach((api, apiIndex) => {
|
2020-10-13 15:00:01 +02:00
|
|
|
const istyle = vtkInteractorStyleRotatableMPRCrosshairs.newInstance();
|
2019-10-18 15:53:43 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
api.setInteractorStyle({
|
|
|
|
|
istyle,
|
2020-10-13 15:00:01 +02:00
|
|
|
configuration: {
|
|
|
|
|
apis,
|
|
|
|
|
apiIndex,
|
|
|
|
|
uid: api.uid,
|
|
|
|
|
},
|
2019-12-02 13:29:04 -03:00
|
|
|
});
|
|
|
|
|
});
|
2020-10-13 15:00:01 +02:00
|
|
|
|
|
|
|
|
const rotatableCrosshairsWidget =
|
|
|
|
|
apis[0].svgWidgets.rotatableCrosshairsWidget;
|
|
|
|
|
|
|
|
|
|
const referenceLines = rotatableCrosshairsWidget.getReferenceLines();
|
|
|
|
|
|
|
|
|
|
// Initilise crosshairs if not initialised.
|
|
|
|
|
if (!referenceLines) {
|
|
|
|
|
rotatableCrosshairsWidget.resetCrosshairs(apis, 0);
|
|
|
|
|
}
|
2019-12-02 13:29:04 -03:00
|
|
|
},
|
2020-04-09 07:06:41 -04:00
|
|
|
enableLevelTool: () => {
|
2019-12-02 13:29:04 -03:00
|
|
|
function updateVOI(apis, windowWidth, windowCenter) {
|
|
|
|
|
apis.forEach(api => {
|
|
|
|
|
api.updateVOI(windowWidth, windowCenter);
|
|
|
|
|
});
|
2019-10-01 05:06:56 -03:00
|
|
|
}
|
|
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
const throttledUpdateVOIs = throttle(updateVOI, 16, { trailing: true }); // ~ 60 fps
|
2019-07-18 13:39:00 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
const callbacks = {
|
|
|
|
|
setOnLevelsChanged: ({ windowCenter, windowWidth }) => {
|
|
|
|
|
apis.forEach(api => {
|
|
|
|
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
2019-07-18 13:39:00 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
renderWindow.render();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
throttledUpdateVOIs(apis, windowWidth, windowCenter);
|
2019-10-18 11:43:23 +02:00
|
|
|
},
|
2019-12-02 13:29:04 -03:00
|
|
|
};
|
2019-10-18 11:43:23 +02:00
|
|
|
|
2020-10-15 16:15:28 +02:00
|
|
|
apis.forEach((api, apiIndex) => {
|
2020-04-09 07:06:41 -04:00
|
|
|
const istyle = vtkInteractorStyleMPRWindowLevel.newInstance();
|
2019-06-15 11:51:56 +02:00
|
|
|
|
2020-10-15 16:15:28 +02:00
|
|
|
api.setInteractorStyle({
|
|
|
|
|
istyle,
|
|
|
|
|
callbacks,
|
|
|
|
|
configuration: { apis, apiIndex, uid: api.uid },
|
|
|
|
|
});
|
2019-12-02 13:29:04 -03:00
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
setSlabThickness: ({ slabThickness }) => {
|
|
|
|
|
apis.forEach(api => {
|
|
|
|
|
api.setSlabThickness(slabThickness);
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
changeSlabThickness: ({ change }) => {
|
|
|
|
|
apis.forEach(api => {
|
|
|
|
|
const slabThickness = Math.max(api.getSlabThickness() + change, 0.1);
|
2019-10-18 11:43:23 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
api.setSlabThickness(slabThickness);
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
setBlendModeToComposite: () => {
|
|
|
|
|
apis.forEach(api => {
|
|
|
|
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
|
|
|
|
const istyle = renderWindow.getInteractor().getInteractorStyle();
|
2019-06-15 11:51:56 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
const slabThickness = api.getSlabThickness();
|
2019-06-15 11:51:56 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
const mapper = api.volumes[0].getMapper();
|
|
|
|
|
if (mapper.setBlendModeToComposite) {
|
|
|
|
|
mapper.setBlendModeToComposite();
|
|
|
|
|
}
|
2019-06-15 11:51:56 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
if (istyle.setSlabThickness) {
|
|
|
|
|
istyle.setSlabThickness(slabThickness);
|
|
|
|
|
}
|
|
|
|
|
renderWindow.render();
|
|
|
|
|
});
|
2019-07-18 13:39:00 +02:00
|
|
|
},
|
2019-12-02 13:29:04 -03:00
|
|
|
setBlendModeToMaximumIntensity: () => {
|
|
|
|
|
apis.forEach(api => {
|
|
|
|
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
|
|
|
|
const mapper = api.volumes[0].getMapper();
|
|
|
|
|
if (mapper.setBlendModeToMaximumIntensity) {
|
|
|
|
|
mapper.setBlendModeToMaximumIntensity();
|
|
|
|
|
}
|
|
|
|
|
renderWindow.render();
|
|
|
|
|
});
|
2019-07-18 13:39:00 +02:00
|
|
|
},
|
2019-12-02 13:29:04 -03:00
|
|
|
setBlendMode: ({ blendMode }) => {
|
|
|
|
|
apis.forEach(api => {
|
|
|
|
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
2019-06-15 11:51:56 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
api.volumes[0].getMapper().setBlendMode(blendMode);
|
|
|
|
|
|
|
|
|
|
renderWindow.render();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
mpr2d: async ({ viewports }) => {
|
|
|
|
|
// TODO push a lot of this backdoor logic lower down to the library level.
|
|
|
|
|
const displaySet =
|
|
|
|
|
viewports.viewportSpecificData[viewports.activeViewportIndex];
|
|
|
|
|
|
|
|
|
|
// Get current VOI if cornerstone viewport.
|
|
|
|
|
const cornerstoneVOI = getVOIFromCornerstoneViewport();
|
2020-12-03 14:11:14 -03:00
|
|
|
defaultVOI = cornerstoneVOI;
|
2019-12-02 13:29:04 -03:00
|
|
|
|
|
|
|
|
const viewportProps = [
|
|
|
|
|
{
|
|
|
|
|
//Axial
|
|
|
|
|
orientation: {
|
|
|
|
|
sliceNormal: [0, 0, 1],
|
|
|
|
|
viewUp: [0, -1, 0],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
2020-01-07 23:41:16 +01:00
|
|
|
// Sagittal
|
2019-12-02 13:29:04 -03:00
|
|
|
orientation: {
|
|
|
|
|
sliceNormal: [1, 0, 0],
|
|
|
|
|
viewUp: [0, 0, 1],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// Coronal
|
|
|
|
|
orientation: {
|
|
|
|
|
sliceNormal: [0, 1, 0],
|
|
|
|
|
viewUp: [0, 0, 1],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
];
|
2019-10-18 15:53:43 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
try {
|
|
|
|
|
apis = await setMPRLayout(displaySet, viewportProps, 1, 3);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
throw new Error(error);
|
|
|
|
|
}
|
2019-10-18 15:53:43 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
if (cornerstoneVOI) {
|
|
|
|
|
setVOI(cornerstoneVOI);
|
|
|
|
|
}
|
2019-10-18 15:53:43 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
// Add widgets and set default interactorStyle of each viewport.
|
|
|
|
|
apis.forEach((api, apiIndex) => {
|
|
|
|
|
api.addSVGWidget(
|
2020-10-13 15:00:01 +02:00
|
|
|
vtkSVGRotatableCrosshairsWidget.newInstance(),
|
|
|
|
|
'rotatableCrosshairsWidget'
|
2019-12-02 13:29:04 -03:00
|
|
|
);
|
2019-10-18 15:53:43 +02:00
|
|
|
|
2020-01-30 15:26:06 -03:00
|
|
|
const uid = api.uid;
|
2020-10-13 15:00:01 +02:00
|
|
|
const istyle = vtkInteractorStyleRotatableMPRCrosshairs.newInstance();
|
2019-10-18 15:53:43 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
api.setInteractorStyle({
|
|
|
|
|
istyle,
|
2020-01-30 15:26:06 -03:00
|
|
|
configuration: { apis, apiIndex, uid },
|
2019-12-02 13:29:04 -03:00
|
|
|
});
|
2020-10-13 15:00:01 +02:00
|
|
|
|
|
|
|
|
api.svgWidgets.rotatableCrosshairsWidget.setApiIndex(apiIndex);
|
|
|
|
|
api.svgWidgets.rotatableCrosshairsWidget.setApis(apis);
|
2019-12-02 13:29:04 -03:00
|
|
|
});
|
2020-10-13 15:00:01 +02:00
|
|
|
|
2020-10-13 18:23:14 +02:00
|
|
|
const firstApi = apis[0];
|
|
|
|
|
|
2020-10-13 15:00:01 +02:00
|
|
|
// Initialise crosshairs
|
|
|
|
|
apis[0].svgWidgets.rotatableCrosshairsWidget.resetCrosshairs(apis, 0);
|
2020-10-13 18:23:14 +02:00
|
|
|
|
|
|
|
|
// Check if we have full WebGL 2 support
|
|
|
|
|
const openGLRenderWindow = apis[0].genericRenderWindow.getOpenGLRenderWindow();
|
|
|
|
|
|
|
|
|
|
if (!openGLRenderWindow.getWebgl2()) {
|
|
|
|
|
// Throw a warning if we don't have WebGL 2 support,
|
|
|
|
|
// And the volume is too big to fit in a 2D texture
|
|
|
|
|
|
|
|
|
|
const openGLContext = openGLRenderWindow.getContext();
|
|
|
|
|
const maxTextureSizeInBytes = openGLContext.getParameter(
|
|
|
|
|
openGLContext.MAX_TEXTURE_SIZE
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const maxBufferLengthFloat32 =
|
|
|
|
|
(maxTextureSizeInBytes * maxTextureSizeInBytes) / 4;
|
|
|
|
|
|
|
|
|
|
const dimensions = firstApi.volumes[0]
|
|
|
|
|
.getMapper()
|
|
|
|
|
.getInputData()
|
|
|
|
|
.getDimensions();
|
|
|
|
|
|
|
|
|
|
const volumeLength = dimensions[0] * dimensions[1] * dimensions[2];
|
|
|
|
|
|
|
|
|
|
if (volumeLength > maxBufferLengthFloat32) {
|
2021-02-25 18:29:28 -03:00
|
|
|
const message =
|
|
|
|
|
'This volume is too large to fit in WebGL 1 textures and will display incorrectly. Please use a different browser to view this data';
|
|
|
|
|
LoggerService.error({ message });
|
2020-10-13 18:23:14 +02:00
|
|
|
UINotificationService.show({
|
|
|
|
|
title: 'Browser does not support WebGL 2',
|
2021-02-25 18:29:28 -03:00
|
|
|
message,
|
2020-10-13 18:23:14 +02:00
|
|
|
type: 'error',
|
|
|
|
|
autoClose: false,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-12-02 13:29:04 -03:00
|
|
|
},
|
|
|
|
|
};
|
2019-10-18 15:53:43 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
window.vtkActions = actions;
|
2019-10-18 15:53:43 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
const definitions = {
|
2020-05-06 14:17:44 -03:00
|
|
|
requestNewSegmentation: {
|
|
|
|
|
commandFn: actions.requestNewSegmentation,
|
|
|
|
|
storeContexts: ['viewports'],
|
|
|
|
|
options: {},
|
|
|
|
|
},
|
|
|
|
|
jumpToSlice: {
|
|
|
|
|
commandFn: actions.jumpToSlice,
|
|
|
|
|
storeContexts: ['viewports'],
|
|
|
|
|
options: {},
|
|
|
|
|
},
|
|
|
|
|
setSegmentationConfiguration: {
|
|
|
|
|
commandFn: actions.setSegmentationConfiguration,
|
|
|
|
|
storeContexts: ['viewports'],
|
|
|
|
|
options: {},
|
|
|
|
|
},
|
|
|
|
|
setSegmentConfiguration: {
|
|
|
|
|
commandFn: actions.setSegmentConfiguration,
|
|
|
|
|
storeContexts: ['viewports'],
|
|
|
|
|
options: {},
|
|
|
|
|
},
|
2019-12-02 13:29:04 -03:00
|
|
|
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,
|
|
|
|
|
options: {},
|
|
|
|
|
},
|
|
|
|
|
enableCrosshairsTool: {
|
|
|
|
|
commandFn: actions.enableCrosshairsTool,
|
|
|
|
|
options: {},
|
|
|
|
|
},
|
|
|
|
|
enableLevelTool: {
|
|
|
|
|
commandFn: actions.enableLevelTool,
|
|
|
|
|
options: {},
|
|
|
|
|
},
|
2020-10-13 15:00:01 +02:00
|
|
|
resetMPRView: {
|
|
|
|
|
commandFn: actions.resetMPRView,
|
|
|
|
|
options: {},
|
|
|
|
|
},
|
2019-12-02 13:29:04 -03:00
|
|
|
setBlendModeToComposite: {
|
|
|
|
|
commandFn: actions.setBlendModeToComposite,
|
|
|
|
|
options: { blendMode: BlendMode.COMPOSITE_BLEND },
|
|
|
|
|
},
|
|
|
|
|
setBlendModeToMaximumIntensity: {
|
|
|
|
|
commandFn: actions.setBlendModeToMaximumIntensity,
|
|
|
|
|
options: { blendMode: BlendMode.MAXIMUM_INTENSITY_BLEND },
|
|
|
|
|
},
|
|
|
|
|
setBlendModeToMinimumIntensity: {
|
|
|
|
|
commandFn: actions.setBlendMode,
|
|
|
|
|
options: { blendMode: BlendMode.MINIMUM_INTENSITY_BLEND },
|
|
|
|
|
},
|
|
|
|
|
setBlendModeToAverageIntensity: {
|
|
|
|
|
commandFn: actions.setBlendMode,
|
|
|
|
|
options: { blendMode: BlendMode.AVERAGE_INTENSITY_BLEND },
|
|
|
|
|
},
|
|
|
|
|
setSlabThickness: {
|
|
|
|
|
// TODO: How do we pass in a function argument?
|
|
|
|
|
commandFn: actions.setSlabThickness,
|
|
|
|
|
options: {},
|
|
|
|
|
},
|
|
|
|
|
increaseSlabThickness: {
|
|
|
|
|
commandFn: actions.changeSlabThickness,
|
|
|
|
|
options: {
|
|
|
|
|
change: 3,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
decreaseSlabThickness: {
|
|
|
|
|
commandFn: actions.changeSlabThickness,
|
|
|
|
|
options: {
|
|
|
|
|
change: -3,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
mpr2d: {
|
|
|
|
|
commandFn: actions.mpr2d,
|
|
|
|
|
storeContexts: ['viewports'],
|
|
|
|
|
options: {},
|
|
|
|
|
context: 'VIEWER',
|
|
|
|
|
},
|
2020-01-30 15:26:06 -03:00
|
|
|
getVtkApiForViewportIndex: {
|
|
|
|
|
commandFn: actions.getVtkApis,
|
|
|
|
|
context: 'VIEWER',
|
|
|
|
|
},
|
2019-12-02 13:29:04 -03:00
|
|
|
};
|
2019-10-18 15:53:43 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
return {
|
|
|
|
|
definitions,
|
|
|
|
|
defaultContext: 'ACTIVE_VIEWPORT::VTK',
|
|
|
|
|
};
|
|
|
|
|
};
|
2019-10-18 15:53:43 +02:00
|
|
|
|
2019-12-02 13:29:04 -03:00
|
|
|
export default commandsModule;
|