Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6629bda75 | ||
|
|
588bcc9d17 | ||
|
|
c31f8f9d65 | ||
|
|
c0280e28ca | ||
|
|
7c3ac387ef | ||
|
|
d6f79d9ddf |
No files matched your search
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.4.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.4.8...@ohif/extension-dicom-rt@0.4.9) (2020-10-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.4.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.4.7...@ohif/extension-dicom-rt@0.4.8) (2020-10-07)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-rt",
|
||||
"version": "0.4.8",
|
||||
"version": "0.4.9",
|
||||
"description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -32,6 +32,7 @@
|
||||
"cornerstone-core": "^2.2.8",
|
||||
"cornerstone-tools": "^4.20.1",
|
||||
"dcmjs": "0.16.5",
|
||||
"gl-matrix": "^3.3.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
|
||||
@@ -4,6 +4,7 @@ import cornerstone from 'cornerstone-core';
|
||||
import cornerstoneTools from 'cornerstone-tools';
|
||||
import transformPointsToImagePlane from './utils/transformPointsToImagePlane';
|
||||
import TOOL_NAMES from './utils/toolNames';
|
||||
import { vec3 } from 'gl-matrix';
|
||||
|
||||
const dicomlab2RGB = dcmjs.data.Colors.dicomlab2RGB;
|
||||
const globalImageIdSpecificToolStateManager =
|
||||
@@ -66,8 +67,6 @@ export default async function loadRTStruct(
|
||||
|
||||
const rtStructDisplayToolName = TOOL_NAMES.RTSTRUCT_DISPLAY_TOOL;
|
||||
|
||||
debugger;
|
||||
|
||||
for (let i = 0; i < ROIContourSequence.length; i++) {
|
||||
const ROIContour = ROIContourSequence[i];
|
||||
const { ReferencedROINumber, ContourSequence } = ROIContour;
|
||||
@@ -88,7 +87,14 @@ export default async function loadRTStruct(
|
||||
ContourGeometricType,
|
||||
} = ContourSequenceArray[c];
|
||||
|
||||
const sopInstanceUID = ContourImageSequence.ReferencedSOPInstanceUID;
|
||||
const sopInstanceUID = ContourImageSequence
|
||||
? ContourImageSequence.ReferencedSOPInstanceUID
|
||||
: _getClosestSOPInstanceUID(
|
||||
ContourData,
|
||||
ContourGeometricType,
|
||||
NumberOfContourPoints,
|
||||
imageIdSopInstanceUidPairs
|
||||
);
|
||||
const imageId = _getImageId(imageIdSopInstanceUidPairs, sopInstanceUID);
|
||||
const imageIdSpecificToolData = _getOrCreateImageIdSpecificToolData(
|
||||
toolState,
|
||||
@@ -326,3 +332,78 @@ function _getImageIdSopInstanceUidPairsForDisplaySet(
|
||||
function _toArray(objOrArray) {
|
||||
return Array.isArray(objOrArray) ? objOrArray : [objOrArray];
|
||||
}
|
||||
|
||||
function _getClosestSOPInstanceUID(
|
||||
ContourData,
|
||||
ContourGeometricType,
|
||||
NumberOfContourPoints,
|
||||
imageIdSopInstanceUidPairs
|
||||
) {
|
||||
const closest = {
|
||||
distance: Infinity,
|
||||
sopInstanceUID: null,
|
||||
};
|
||||
|
||||
let point;
|
||||
|
||||
switch (ContourGeometricType) {
|
||||
case 'POINT':
|
||||
point = ContourData;
|
||||
break;
|
||||
case 'CLOSED_PLANAR':
|
||||
case 'OPEN_PLANAR':
|
||||
// These are defined as planar, so get the of the region to get the
|
||||
// Best mapping to a plane even if its potentially off center.
|
||||
|
||||
point = [0, 0, 0];
|
||||
for (let p = 0; p < NumberOfContourPoints * 3; p += 3) {
|
||||
point[0] += ContourData[p];
|
||||
point[1] += ContourData[p + 1];
|
||||
point[2] += ContourData[p + 2];
|
||||
}
|
||||
|
||||
point[0] /= NumberOfContourPoints;
|
||||
point[1] /= NumberOfContourPoints;
|
||||
point[2] /= NumberOfContourPoints;
|
||||
}
|
||||
|
||||
imageIdSopInstanceUidPairs.forEach(pair => {
|
||||
const { imageId } = pair;
|
||||
|
||||
const imagePlaneModule = cornerstone.metaData.get(
|
||||
'imagePlaneModule',
|
||||
imageId
|
||||
);
|
||||
|
||||
const distance = distanceFromPointToPlane(point, imagePlaneModule);
|
||||
|
||||
if (distance < closest.distance) {
|
||||
closest.distance = distance;
|
||||
closest.sopInstanceUID = pair.sopInstanceUID;
|
||||
}
|
||||
});
|
||||
|
||||
return closest.sopInstanceUID;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number[3]} P - The point
|
||||
* @param {object} imagePlaneModule The cornerstone metadata object for the imagePlane
|
||||
*/
|
||||
function distanceFromPointToPlane(P, imagePlaneModule) {
|
||||
const {
|
||||
rowCosines,
|
||||
columnCosines,
|
||||
imagePositionPatient: Q,
|
||||
} = imagePlaneModule;
|
||||
|
||||
let N = [];
|
||||
vec3.cross(N, rowCosines, columnCosines);
|
||||
|
||||
const [A, B, C] = N;
|
||||
|
||||
const D = -A * Q[0] - B * Q[1] - C * Q[2];
|
||||
|
||||
return Math.abs(A * P[0] + B * P[1] + C * P[2] + D); // Denominator is sqrt(A**2 + B**2 + C**2) which is 1 as its a normal vector
|
||||
}
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.7.12](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.7.11...@ohif/extension-vtk@1.7.12) (2020-10-13)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.7.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.7.10...@ohif/extension-vtk@1.7.11) (2020-10-13)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.7.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.7.9...@ohif/extension-vtk@1.7.10) (2020-10-07)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-vtk",
|
||||
"version": "1.7.10",
|
||||
"version": "1.7.12",
|
||||
"description": "OHIF extension for VTK.js",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -50,7 +50,7 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"react-vtkjs-viewport": "^0.10.4"
|
||||
"react-vtkjs-viewport": "^0.13.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ohif/core": "^2.10.7",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import throttle from 'lodash.throttle';
|
||||
import {
|
||||
vtkInteractorStyleMPRCrosshairs,
|
||||
vtkInteractorStyleMPRWindowLevel,
|
||||
vtkInteractorStyleRotatableMPRCrosshairs,
|
||||
vtkSVGRotatableCrosshairsWidget,
|
||||
vtkInteractorStyleMPRRotate,
|
||||
vtkSVGCrosshairsWidget,
|
||||
} from 'react-vtkjs-viewport';
|
||||
import { getImageData } from 'react-vtkjs-viewport';
|
||||
import { vec3 } from 'gl-matrix';
|
||||
@@ -11,11 +11,10 @@ import setMPRLayout from './utils/setMPRLayout.js';
|
||||
import setViewportToVTK from './utils/setViewportToVTK.js';
|
||||
import Constants from 'vtk.js/Sources/Rendering/Core/VolumeMapper/Constants.js';
|
||||
import OHIFVTKViewport from './OHIFVTKViewport';
|
||||
import vtkCoordinate from 'vtk.js/Sources/Rendering/Core/Coordinate';
|
||||
|
||||
const { BlendMode } = Constants;
|
||||
|
||||
const commandsModule = ({ commandsManager }) => {
|
||||
const commandsModule = ({ commandsManager, UINotificationService }) => {
|
||||
// TODO: Put this somewhere else
|
||||
let apis = {};
|
||||
|
||||
@@ -119,6 +118,14 @@ const commandsModule = ({ commandsManager }) => {
|
||||
getVtkApis: ({ index }) => {
|
||||
return apis[index];
|
||||
},
|
||||
resetMPRView() {
|
||||
apis.forEach(api => {
|
||||
api.resetOrientation();
|
||||
});
|
||||
|
||||
// Reset the crosshairs
|
||||
apis[0].svgWidgets.rotatableCrosshairsWidget.resetCrosshairs(apis, 0);
|
||||
},
|
||||
axial: async ({ viewports }) => {
|
||||
const api = await _getActiveViewportVTKApi(viewports);
|
||||
|
||||
@@ -165,7 +172,7 @@ const commandsModule = ({ commandsManager }) => {
|
||||
segmentNumber,
|
||||
frameIndex,
|
||||
frame,
|
||||
done = () => { }
|
||||
done = () => {},
|
||||
}) => {
|
||||
let api = apis[viewports.activeViewportIndex];
|
||||
|
||||
@@ -180,10 +187,13 @@ const commandsModule = ({ commandsManager }) => {
|
||||
displaySetInstanceUID,
|
||||
SOPClassUID,
|
||||
SOPInstanceUID,
|
||||
frameIndex,
|
||||
frameIndex
|
||||
);
|
||||
|
||||
const imageDataObject = getImageData(stack.imageIds, displaySetInstanceUID);
|
||||
const imageDataObject = getImageData(
|
||||
stack.imageIds,
|
||||
displaySetInstanceUID
|
||||
);
|
||||
|
||||
let pixelIndex = 0;
|
||||
let x = 0;
|
||||
@@ -209,9 +219,12 @@ const commandsModule = ({ commandsManager }) => {
|
||||
y /= count;
|
||||
|
||||
const position = [x, y, frameIndex];
|
||||
const worldPos = _convertModelToWorldSpace(position, imageDataObject.vtkImageData);
|
||||
const worldPos = _convertModelToWorldSpace(
|
||||
position,
|
||||
imageDataObject.vtkImageData
|
||||
);
|
||||
|
||||
api.svgWidgets.crosshairsWidget.moveCrosshairs(worldPos, apis);
|
||||
api.svgWidgets.rotatableCrosshairsWidget.moveCrosshairs(worldPos, apis);
|
||||
done();
|
||||
},
|
||||
setSegmentationConfiguration: async ({
|
||||
@@ -254,21 +267,35 @@ const commandsModule = ({ commandsManager }) => {
|
||||
await Promise.all(promises);
|
||||
},
|
||||
enableRotateTool: () => {
|
||||
apis.forEach(api => {
|
||||
apis.forEach((api, apiIndex) => {
|
||||
const istyle = vtkInteractorStyleMPRRotate.newInstance();
|
||||
|
||||
api.setInteractorStyle({ istyle });
|
||||
api.setInteractorStyle({ istyle, configuration: { apis, apiIndex } });
|
||||
});
|
||||
},
|
||||
enableCrosshairsTool: () => {
|
||||
apis.forEach((api, apiIndex) => {
|
||||
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
|
||||
const istyle = vtkInteractorStyleRotatableMPRCrosshairs.newInstance();
|
||||
|
||||
api.setInteractorStyle({
|
||||
istyle,
|
||||
configuration: { apis, apiIndex },
|
||||
configuration: {
|
||||
apis,
|
||||
apiIndex,
|
||||
uid: api.uid,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const rotatableCrosshairsWidget =
|
||||
apis[0].svgWidgets.rotatableCrosshairsWidget;
|
||||
|
||||
const referenceLines = rotatableCrosshairsWidget.getReferenceLines();
|
||||
|
||||
// Initilise crosshairs if not initialised.
|
||||
if (!referenceLines) {
|
||||
rotatableCrosshairsWidget.resetCrosshairs(apis, 0);
|
||||
}
|
||||
},
|
||||
enableLevelTool: () => {
|
||||
function updateVOI(apis, windowWidth, windowCenter) {
|
||||
@@ -391,18 +418,59 @@ const commandsModule = ({ commandsManager }) => {
|
||||
// Add widgets and set default interactorStyle of each viewport.
|
||||
apis.forEach((api, apiIndex) => {
|
||||
api.addSVGWidget(
|
||||
vtkSVGCrosshairsWidget.newInstance(),
|
||||
'crosshairsWidget'
|
||||
vtkSVGRotatableCrosshairsWidget.newInstance(),
|
||||
'rotatableCrosshairsWidget'
|
||||
);
|
||||
|
||||
const uid = api.uid;
|
||||
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
|
||||
const istyle = vtkInteractorStyleRotatableMPRCrosshairs.newInstance();
|
||||
|
||||
api.setInteractorStyle({
|
||||
istyle,
|
||||
configuration: { apis, apiIndex, uid },
|
||||
});
|
||||
|
||||
api.svgWidgets.rotatableCrosshairsWidget.setApiIndex(apiIndex);
|
||||
api.svgWidgets.rotatableCrosshairsWidget.setApis(apis);
|
||||
});
|
||||
|
||||
const firstApi = apis[0];
|
||||
|
||||
// Initialise crosshairs
|
||||
apis[0].svgWidgets.rotatableCrosshairsWidget.resetCrosshairs(apis, 0);
|
||||
|
||||
// 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) {
|
||||
UINotificationService.show({
|
||||
title: 'Browser does not support WebGL 2',
|
||||
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',
|
||||
type: 'error',
|
||||
autoClose: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -456,6 +524,10 @@ const commandsModule = ({ commandsManager }) => {
|
||||
commandFn: actions.enableLevelTool,
|
||||
options: {},
|
||||
},
|
||||
resetMPRView: {
|
||||
commandFn: actions.resetMPRView,
|
||||
options: {},
|
||||
},
|
||||
setBlendModeToComposite: {
|
||||
commandFn: actions.setBlendModeToComposite,
|
||||
options: { blendMode: BlendMode.COMPOSITE_BLEND },
|
||||
|
||||
@@ -27,8 +27,9 @@ const vtkExtension = {
|
||||
getToolbarModule() {
|
||||
return toolbarModule;
|
||||
},
|
||||
getCommandsModule({ commandsManager }) {
|
||||
return commandsModule({ commandsManager });
|
||||
getCommandsModule({ commandsManager, servicesManager }) {
|
||||
const { UINotificationService } = servicesManager.services;
|
||||
return commandsModule({ commandsManager, UINotificationService });
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -25,6 +25,15 @@ const definitions = [
|
||||
commandName: 'enableLevelTool',
|
||||
commandOptions: {},
|
||||
},
|
||||
{
|
||||
id: 'Reset',
|
||||
label: 'Reset',
|
||||
icon: 'reset',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'resetMPRView',
|
||||
commandOptions: {},
|
||||
},
|
||||
{
|
||||
id: 'Rotate',
|
||||
label: 'Rotate',
|
||||
@@ -131,4 +140,4 @@ const definitions = [
|
||||
export default {
|
||||
definitions,
|
||||
defaultContext: 'ACTIVE_VIEWPORT::VTK',
|
||||
}
|
||||
};
|
||||
@@ -3,6 +3,30 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [4.5.20](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.5.19...@ohif/viewer@4.5.20) (2020-10-13)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.5.19](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.5.18...@ohif/viewer@4.5.19) (2020-10-13)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.5.18](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.5.17...@ohif/viewer@4.5.18) (2020-10-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.5.17](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.5.16...@ohif/viewer@4.5.17) (2020-10-07)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/viewer",
|
||||
"version": "4.5.17",
|
||||
"version": "4.5.20",
|
||||
"description": "OHIF Viewer",
|
||||
"author": "OHIF Contributors",
|
||||
"license": "MIT",
|
||||
@@ -54,10 +54,10 @@
|
||||
"@ohif/extension-dicom-html": "^1.2.9",
|
||||
"@ohif/extension-dicom-microscopy": "^0.51.3",
|
||||
"@ohif/extension-dicom-pdf": "^1.0.6",
|
||||
"@ohif/extension-dicom-rt": "^0.4.8",
|
||||
"@ohif/extension-dicom-rt": "^0.4.9",
|
||||
"@ohif/extension-dicom-segmentation": "^0.4.7",
|
||||
"@ohif/extension-lesion-tracker": "^0.2.1",
|
||||
"@ohif/extension-vtk": "^1.7.10",
|
||||
"@ohif/extension-vtk": "^1.7.12",
|
||||
"@ohif/i18n": "^0.52.8",
|
||||
"@ohif/ui": "^1.5.6",
|
||||
"@tanem/react-nprogress": "^1.1.25",
|
||||
|
||||
@@ -6539,13 +6539,14 @@ dateformat@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
|
||||
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
|
||||
|
||||
dcmjs@0.16.3:
|
||||
version "0.16.3"
|
||||
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.16.3.tgz#ef44ccc4988a473fd40e1601122c57f2babded11"
|
||||
integrity sha512-pMQGifMpMW9i9ufCdUoEPHrLWnm6ilnpfLqUc+PPoWr7OnuBhiRVRunbv39oTtFDZOLhQ2Ampm7UrkidWAbqiA==
|
||||
dcmjs@0.16.5:
|
||||
version "0.16.5"
|
||||
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.16.5.tgz#05aa0c8d539a4d5777f996b9b796932d1f848993"
|
||||
integrity sha512-Y92KAx4JSfev79xjYhaukQZjWgyJlaHnXSTdbtkGkJAHERjIakgHcCIPmACuVmtW/nHViTu04zIGAB0pmZ4GaA==
|
||||
dependencies:
|
||||
"@babel/polyfill" "^7.8.3"
|
||||
"@babel/runtime" "^7.8.4"
|
||||
gl-matrix "^3.1.0"
|
||||
loglevelnext "^3.0.1"
|
||||
ndarray "^1.0.19"
|
||||
|
||||
@@ -16060,10 +16061,10 @@ react-transition-group@^4.1.1:
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
react-vtkjs-viewport@^0.10.4:
|
||||
version "0.10.4"
|
||||
resolved "https://registry.yarnpkg.com/react-vtkjs-viewport/-/react-vtkjs-viewport-0.10.4.tgz#eb038b0bf5c384bd3b45f2a0a57f52c682607fea"
|
||||
integrity sha512-J1WxwHbjlttEWvdkM33DJx8QxHr3J7HR6wFSDVBDKY7McK2lwDAdRN8wSqy2BVNILlyurUUVwg2tK2txiJ+tuw==
|
||||
react-vtkjs-viewport@^0.13.1:
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-vtkjs-viewport/-/react-vtkjs-viewport-0.13.1.tgz#11b69873377cd66b6522079eb05d54317c2e8903"
|
||||
integrity sha512-P2jmv0rZtas9+pZCzVBr0R/EqmXpWqPP8JA1DScwKgFoYj4ApqnQeLXSVz2VHXi58q87k2uay9k7xcchLiStrQ==
|
||||
dependencies:
|
||||
date-fns "^2.2.1"
|
||||
gl-matrix "^3.1.0"
|
||||
|
||||
Reference in new issue
Block a user