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:
Joe Boccanfuso authored and GitHub committed 2023-03-21 16:09:50 -04:00
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 };
@@ -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`
);