feat(VolumeViewport3D): add volumeViewport3Dd sample hp (#3241)
* feat(VolumeViewport3D) - importing and using Cornerstone 3D's VolumeViewport3D viewport - added mprAnd3DVolumeViewport hanging protocol - allowed for W/L preset to be applied to a viewport * Updated cornerstone core dependency * Removed pan and zoom from the volume3d tool group so that tackballrotate is never dropped for that viewport. Removed overlays from 3D volume viewport. Throw an error and display a message whenever an unapplicable tool is selected. * The default/initial orientation for a 3D volume viewport can now be set. Using the 'interleaveTopToBottom' image load strategy for the 'mprAnd3DVolumeViewport' hanging protocol. * Do not set orientation for stack viewport.
This commit is contained in:
1 parent
5ad5bd232d
commit
550918dd31
12 files changed
+230
-17
No files matched your search
@@ -181,6 +181,17 @@ const commandsModule = ({
|
||||
return;
|
||||
}
|
||||
|
||||
if (!toolGroup.getToolInstance(toolName)) {
|
||||
uiNotificationService.show({
|
||||
title: `${toolName} tool`,
|
||||
message: `The ${toolName} tool is not available in this viewport.`,
|
||||
type: 'info',
|
||||
duration: 3000,
|
||||
});
|
||||
|
||||
throw new Error(`ToolGroup ${toolGroup.id} does not have this tool.`);
|
||||
}
|
||||
|
||||
const activeToolName = toolGroup.getActivePrimaryMouseButtonTool();
|
||||
|
||||
if (activeToolName) {
|
||||
|
||||
@@ -160,12 +160,147 @@ const mpr: Types.HangingProtocol.Protocol = {
|
||||
],
|
||||
};
|
||||
|
||||
const mprAnd3DVolumeViewport = {
|
||||
id: 'mprAnd3DVolumeViewport',
|
||||
locked: true,
|
||||
hasUpdatedPriorsInformation: false,
|
||||
name: 'mpr',
|
||||
createdDate: '2023-03-15T10:29:44.894Z',
|
||||
modifiedDate: '2023-03-15T10:29:44.894Z',
|
||||
availableTo: {},
|
||||
editableBy: {},
|
||||
protocolMatchingRules: [],
|
||||
imageLoadStrategy: 'interleaveTopToBottom',
|
||||
displaySetSelectors: {
|
||||
mprDisplaySet: {
|
||||
seriesMatchingRules: [
|
||||
{
|
||||
weight: 1,
|
||||
attribute: 'isReconstructable',
|
||||
constraint: {
|
||||
equals: {
|
||||
value: true,
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
stages: [
|
||||
{
|
||||
id: 'mpr3Stage',
|
||||
name: 'mpr',
|
||||
viewportStructure: {
|
||||
layoutType: 'grid',
|
||||
properties: {
|
||||
rows: 2,
|
||||
columns: 2,
|
||||
},
|
||||
},
|
||||
viewports: [
|
||||
{
|
||||
viewportOptions: {
|
||||
toolGroupId: 'mpr',
|
||||
viewportType: 'volume',
|
||||
orientation: 'axial',
|
||||
initialImageOptions: {
|
||||
preset: 'middle',
|
||||
},
|
||||
syncGroups: [
|
||||
{
|
||||
type: 'voi',
|
||||
id: 'mpr',
|
||||
source: true,
|
||||
target: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
displaySets: [
|
||||
{
|
||||
id: 'mprDisplaySet',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
viewportOptions: {
|
||||
toolGroupId: 'volume3d',
|
||||
viewportType: 'volume3d',
|
||||
orientation: 'coronal',
|
||||
customViewportProps: {
|
||||
hideOverlays: true,
|
||||
},
|
||||
},
|
||||
displaySets: [
|
||||
{
|
||||
id: 'mprDisplaySet',
|
||||
options: {
|
||||
presetName: 'CT-Bone',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
viewportOptions: {
|
||||
toolGroupId: 'mpr',
|
||||
viewportType: 'volume',
|
||||
orientation: 'coronal',
|
||||
initialImageOptions: {
|
||||
preset: 'middle',
|
||||
},
|
||||
syncGroups: [
|
||||
{
|
||||
type: 'voi',
|
||||
id: 'mpr',
|
||||
source: true,
|
||||
target: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
displaySets: [
|
||||
{
|
||||
id: 'mprDisplaySet',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
viewportOptions: {
|
||||
toolGroupId: 'mpr',
|
||||
viewportType: 'volume',
|
||||
orientation: 'sagittal',
|
||||
initialImageOptions: {
|
||||
preset: 'middle',
|
||||
},
|
||||
syncGroups: [
|
||||
{
|
||||
type: 'voi',
|
||||
id: 'mpr',
|
||||
source: true,
|
||||
target: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
displaySets: [
|
||||
{
|
||||
id: 'mprDisplaySet',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
function getHangingProtocolModule() {
|
||||
return [
|
||||
{
|
||||
id: 'mpr',
|
||||
protocol: mpr,
|
||||
},
|
||||
{
|
||||
id: mprAnd3DVolumeViewport.id,
|
||||
protocol: mprAnd3DVolumeViewport,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
addTool,
|
||||
annotation,
|
||||
ReferenceLinesTool,
|
||||
TrackballRotateTool,
|
||||
} from '@cornerstonejs/tools';
|
||||
|
||||
import CalibrationLineTool from './tools/CalibrationLineTool';
|
||||
@@ -51,6 +52,7 @@ export default function initCornerstoneTools(configuration = {}) {
|
||||
addTool(SegmentationDisplayTool);
|
||||
addTool(ReferenceLinesTool);
|
||||
addTool(CalibrationLineTool);
|
||||
addTool(TrackballRotateTool);
|
||||
|
||||
// Modify annotation tools to use dashed lines on SR
|
||||
const annotationStyle = {
|
||||
@@ -90,6 +92,7 @@ const toolNames = {
|
||||
SegmentationDisplay: SegmentationDisplayTool.toolName,
|
||||
ReferenceLines: ReferenceLinesTool.toolName,
|
||||
CalibrationLine: CalibrationLineTool.toolName,
|
||||
TrackballRotateTool: TrackballRotateTool.toolName,
|
||||
};
|
||||
|
||||
export { toolNames };
|
||||
+19
-8
@@ -62,12 +62,20 @@ class CornerstoneCacheService {
|
||||
viewportData = await this._getStackViewportData(
|
||||
dataSource,
|
||||
displaySets,
|
||||
initialImageIndex
|
||||
initialImageIndex,
|
||||
cs3DViewportType
|
||||
);
|
||||
}
|
||||
|
||||
if (cs3DViewportType === Enums.ViewportType.ORTHOGRAPHIC) {
|
||||
viewportData = await this._getVolumeViewportData(dataSource, displaySets);
|
||||
if (
|
||||
cs3DViewportType === Enums.ViewportType.ORTHOGRAPHIC ||
|
||||
cs3DViewportType === Enums.ViewportType.VOLUME_3D
|
||||
) {
|
||||
viewportData = await this._getVolumeViewportData(
|
||||
dataSource,
|
||||
displaySets,
|
||||
cs3DViewportType
|
||||
);
|
||||
}
|
||||
|
||||
viewportData.viewportType = cs3DViewportType;
|
||||
@@ -100,7 +108,8 @@ class CornerstoneCacheService {
|
||||
|
||||
const newViewportData = await this._getVolumeViewportData(
|
||||
dataSource,
|
||||
displaySets
|
||||
displaySets,
|
||||
viewportData.viewportType
|
||||
);
|
||||
|
||||
return newViewportData;
|
||||
@@ -109,7 +118,8 @@ class CornerstoneCacheService {
|
||||
private _getStackViewportData(
|
||||
dataSource,
|
||||
displaySets,
|
||||
initialImageIndex
|
||||
initialImageIndex,
|
||||
viewportType: Enums.ViewportType
|
||||
): StackViewportData {
|
||||
// For Stack Viewport we don't have fusion currently
|
||||
const displaySet = displaySets[0];
|
||||
@@ -126,7 +136,7 @@ class CornerstoneCacheService {
|
||||
const { displaySetInstanceUID, StudyInstanceUID } = displaySet;
|
||||
|
||||
const StackViewportData: StackViewportData = {
|
||||
viewportType: Enums.ViewportType.STACK,
|
||||
viewportType,
|
||||
data: {
|
||||
StudyInstanceUID,
|
||||
displaySetInstanceUID,
|
||||
@@ -143,7 +153,8 @@ class CornerstoneCacheService {
|
||||
|
||||
private async _getVolumeViewportData(
|
||||
dataSource,
|
||||
displaySets
|
||||
displaySets,
|
||||
viewportType: Enums.ViewportType
|
||||
): Promise<VolumeViewportData> {
|
||||
// Todo: Check the cache for multiple scenarios to see if we need to
|
||||
// decache the volume data from other viewports or not
|
||||
@@ -207,7 +218,7 @@ class CornerstoneCacheService {
|
||||
}
|
||||
|
||||
return {
|
||||
viewportType: Enums.ViewportType.ORTHOGRAPHIC,
|
||||
viewportType,
|
||||
data: volumeData,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,7 +6,10 @@ import {
|
||||
getRenderingEngine,
|
||||
utilities as csUtils,
|
||||
VolumeViewport,
|
||||
VolumeViewport3D,
|
||||
cache,
|
||||
utilities,
|
||||
CONSTANTS,
|
||||
} from '@cornerstonejs/core';
|
||||
|
||||
import { utilities as csToolsUtils } from '@cornerstonejs/tools';
|
||||
@@ -682,7 +685,12 @@ class CornerstoneViewportService extends PubSubService
|
||||
}
|
||||
|
||||
_getVOICallbacks(volumeId, displaySetOptions) {
|
||||
const { voi, voiInverted: inverted, colormap } = displaySetOptions;
|
||||
const {
|
||||
voi,
|
||||
voiInverted: inverted,
|
||||
colormap,
|
||||
presetName,
|
||||
} = displaySetOptions;
|
||||
|
||||
const voiCallbackArray = [];
|
||||
|
||||
@@ -707,6 +715,16 @@ class CornerstoneViewportService extends PubSubService
|
||||
);
|
||||
}
|
||||
|
||||
if (presetName) {
|
||||
voiCallbackArray.push(volumeActor => {
|
||||
utilities.applyPreset(
|
||||
volumeActor,
|
||||
CONSTANTS.VIEWPORT_PRESETS.find(preset => {
|
||||
return preset.name === presetName;
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
return voiCallbackArray;
|
||||
}
|
||||
|
||||
@@ -723,7 +741,10 @@ class CornerstoneViewportService extends PubSubService
|
||||
viewportInfo,
|
||||
presentations
|
||||
);
|
||||
} else if (viewport instanceof VolumeViewport) {
|
||||
} else if (
|
||||
viewport instanceof VolumeViewport ||
|
||||
viewport instanceof VolumeViewport3D
|
||||
) {
|
||||
this._setVolumeViewport(
|
||||
viewport,
|
||||
viewportData as VolumeViewportData,
|
||||
|
||||
@@ -63,6 +63,7 @@ export type PublicDisplaySetOptions = {
|
||||
blendMode?: string;
|
||||
slabThickness?: number;
|
||||
colormap?: string;
|
||||
presetName?: string;
|
||||
};
|
||||
|
||||
export type DisplaySetOptions = {
|
||||
@@ -72,6 +73,7 @@ export type DisplaySetOptions = {
|
||||
blendMode?: Enums.BlendModes;
|
||||
slabThickness?: number;
|
||||
colormap?: string;
|
||||
presetName?: string;
|
||||
};
|
||||
|
||||
type VOI = {
|
||||
@@ -84,7 +86,6 @@ export type DisplaySet = {
|
||||
};
|
||||
|
||||
const STACK = 'stack';
|
||||
const VOLUME = 'volume';
|
||||
const DEFAULT_TOOLGROUP_ID = 'default';
|
||||
|
||||
class ViewportInfo {
|
||||
@@ -198,10 +199,8 @@ class ViewportInfo {
|
||||
}
|
||||
|
||||
// map SAGITTAL, AXIAL, CORONAL orientation to be used by cornerstone
|
||||
if (viewportOptionsEntry.viewportType?.toLowerCase() === VOLUME) {
|
||||
if (viewportOptionsEntry.viewportType?.toLowerCase() !== STACK) {
|
||||
orientation = getCornerstoneOrientation(viewportOptionsEntry.orientation);
|
||||
} else {
|
||||
orientation = Enums.OrientationAxis.AXIAL;
|
||||
}
|
||||
|
||||
this.setViewportOptions({
|
||||
@@ -282,6 +281,7 @@ class ViewportInfo {
|
||||
colormap: option.colormap,
|
||||
slabThickness: option.slabThickness,
|
||||
blendMode,
|
||||
presetName: option.presetName,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Enums } from '@cornerstonejs/core';
|
||||
const STACK = 'stack';
|
||||
const VOLUME = 'volume';
|
||||
const ORTHOGRAPHIC = 'orthographic';
|
||||
const VOLUME_3D = 'volume3d';
|
||||
|
||||
export default function getCornerstoneViewportType(
|
||||
viewportType: string
|
||||
@@ -16,6 +17,10 @@ export default function getCornerstoneViewportType(
|
||||
return Enums.ViewportType.ORTHOGRAPHIC;
|
||||
}
|
||||
|
||||
if (lowerViewportType === VOLUME_3D) {
|
||||
return Enums.ViewportType.VOLUME_3D;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Invalid viewport type: ${viewportType}. Valid types are: stack, volume`
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user