feat: Add extensibility for tmtv and segmentation modes
This commit is contained in:
22 files changed
+1192
-879
No files matched your search
@@ -0,0 +1,974 @@
|
||||
import type { Button } from '@ohif/core/types';
|
||||
import { ViewportGridService, ToolbarService } from '@ohif/core';
|
||||
import i18n from 'i18next';
|
||||
|
||||
const { TOOLBAR_SECTIONS } = ToolbarService;
|
||||
|
||||
export const MIN_SEGMENTATION_DRAWING_RADIUS = 0.5;
|
||||
export const MAX_SEGMENTATION_DRAWING_RADIUS = 99.5;
|
||||
|
||||
const setToolActiveToolbar = {
|
||||
commandName: 'setToolActiveToolbar',
|
||||
commandOptions: {
|
||||
toolGroupIds: ['default', 'mpr', 'SRToolGroup', 'volume3d'],
|
||||
},
|
||||
};
|
||||
|
||||
const callbacks = (toolName: string) => [
|
||||
{
|
||||
commandName: 'setViewportForToolConfiguration',
|
||||
commandOptions: {
|
||||
toolName,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Segmentation editing toolbar buttons: the toolbox section containers plus
|
||||
* the labelmap / contour editing tools and utilities. These complement the
|
||||
* general buttons in `cornerstone.toolbarButtons`; together they are the
|
||||
* default button set for the segmentation mode, and modes such as basic /
|
||||
* longitudinal can pull them in via a customization (see
|
||||
* `segmentationEditing.jsonc`).
|
||||
*/
|
||||
const segmentationToolbarButtons: Button[] = [
|
||||
// section containers for the nested toolboxes and toolbars
|
||||
{
|
||||
id: 'BrushTools',
|
||||
uiType: 'ohif.toolBoxButtonGroup',
|
||||
props: {
|
||||
buttonSection: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'LabelMapUtilities',
|
||||
uiType: 'ohif.Toolbar',
|
||||
props: {
|
||||
buttonSection: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'ContourUtilities',
|
||||
uiType: 'ohif.Toolbar',
|
||||
props: {
|
||||
buttonSection: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'LabelMapTools',
|
||||
uiType: 'ohif.toolBoxButtonGroup',
|
||||
props: {
|
||||
buttonSection: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'ContourTools',
|
||||
uiType: 'ohif.toolBoxButtonGroup',
|
||||
props: {
|
||||
buttonSection: true,
|
||||
},
|
||||
},
|
||||
// tool defs
|
||||
{
|
||||
id: 'PlanarFreehandContourSegmentationTool',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
icon: 'icon-tool-freehand-roi',
|
||||
label: i18n.t('Buttons:Freehand Segmentation'),
|
||||
tooltip: i18n.t('Buttons:Freehand Segmentation'),
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['PlanarFreehandContourSegmentationTool'],
|
||||
disabledText: i18n.t('Buttons:Create new segmentation to enable this tool.'),
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Contour',
|
||||
},
|
||||
],
|
||||
commands: [
|
||||
{
|
||||
commandName: 'setToolActiveToolbar',
|
||||
commandOptions: {
|
||||
bindings: [
|
||||
{
|
||||
mouseButton: 1, // Left Click
|
||||
},
|
||||
{
|
||||
mouseButton: 1, // Left Click+Shift to create a hole
|
||||
modifierKey: 16, // Shift
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Contour',
|
||||
},
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: i18n.t('Buttons:Interpolate Contours'),
|
||||
type: 'switch',
|
||||
id: 'planarFreehandInterpolateContours',
|
||||
value: false,
|
||||
commands: {
|
||||
commandName: 'setInterpolationToolConfiguration',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'LivewireContourSegmentationTool',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
icon: 'icon-tool-livewire',
|
||||
label: i18n.t('Buttons:Livewire Contour'),
|
||||
tooltip: i18n.t('Buttons:Livewire Contour'),
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['LivewireContourSegmentationTool'],
|
||||
disabledText: i18n.t('Buttons:Create new segmentation to enable this tool.'),
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Contour',
|
||||
},
|
||||
],
|
||||
commands: [
|
||||
{
|
||||
commandName: 'setToolActiveToolbar',
|
||||
commandOptions: {
|
||||
bindings: [
|
||||
{
|
||||
mouseButton: 1, // Left Click
|
||||
},
|
||||
{
|
||||
mouseButton: 1, // Left Click+Shift to create a hole
|
||||
modifierKey: 16, // Shift
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Contour',
|
||||
},
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: i18n.t('Buttons:Interpolate Contours'),
|
||||
type: 'switch',
|
||||
id: 'livewireInterpolateContours',
|
||||
value: false,
|
||||
commands: {
|
||||
commandName: 'setInterpolationToolConfiguration',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'SplineContourSegmentationTool',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
icon: 'icon-tool-spline-roi',
|
||||
label: i18n.t('Buttons:Spline Contour Segmentation Tool'),
|
||||
tooltip: i18n.t('Buttons:Spline Contour Segmentation Tool'),
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['CatmullRomSplineROI', 'LinearSplineROI', 'BSplineROI'],
|
||||
disabledText: i18n.t('Buttons:Create new segmentation to enable this tool.'),
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Contour',
|
||||
},
|
||||
],
|
||||
commands: [
|
||||
{
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Contour',
|
||||
},
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: i18n.t('Buttons:Spline Type'),
|
||||
type: 'select',
|
||||
id: 'splineTypeSelect',
|
||||
value: 'CatmullRomSplineROI',
|
||||
values: [
|
||||
{
|
||||
id: 'CatmullRomSplineROI',
|
||||
value: 'CatmullRomSplineROI',
|
||||
label: i18n.t('Buttons:Catmull Rom Spline'),
|
||||
},
|
||||
{
|
||||
id: 'LinearSplineROI',
|
||||
value: 'LinearSplineROI',
|
||||
label: i18n.t('Buttons:Linear Spline'),
|
||||
},
|
||||
{ id: 'BSplineROI', value: 'BSplineROI', label: i18n.t('Buttons:B-Spline') },
|
||||
],
|
||||
commands: {
|
||||
commandName: 'setToolActiveToolbar',
|
||||
commandOptions: {
|
||||
bindings: [
|
||||
{
|
||||
mouseButton: 1, // Left Click
|
||||
},
|
||||
{
|
||||
mouseButton: 1, // Left Click+Shift to create a hole
|
||||
modifierKey: 16, // Shift
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: i18n.t('Buttons:Simplified Spline'),
|
||||
type: 'switch',
|
||||
id: 'simplifiedSpline',
|
||||
value: true,
|
||||
commands: {
|
||||
commandName: 'setSimplifiedSplineForSplineContourSegmentationTool',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: i18n.t('Buttons:Interpolate Contours'),
|
||||
type: 'switch',
|
||||
id: 'splineInterpolateContours',
|
||||
value: false,
|
||||
commands: {
|
||||
commandName: 'setInterpolationToolConfiguration',
|
||||
commandOptions: {
|
||||
toolNames: ['CatmullRomSplineROI', 'LinearSplineROI', 'BSplineROI'],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'SculptorTool',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
icon: 'icon-tool-sculptor',
|
||||
label: i18n.t('Buttons:Sculptor Tool'),
|
||||
tooltip: i18n.t('Buttons:Sculptor Tool'),
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['SculptorTool'],
|
||||
disabledText: i18n.t('Buttons:Create new segmentation to enable this tool.'),
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Contour',
|
||||
},
|
||||
],
|
||||
commands: [
|
||||
'setToolActiveToolbar',
|
||||
{
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Contour',
|
||||
},
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: i18n.t('Buttons:Dynamic Cursor Size'),
|
||||
type: 'switch',
|
||||
id: 'dynamicCursorSize',
|
||||
value: true,
|
||||
commands: {
|
||||
commandName: 'setDynamicCursorSizeForSculptorTool',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Brush',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
icon: 'icon-tool-brush',
|
||||
label: i18n.t('Buttons:Brush'),
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['CircularBrush', 'SphereBrush'],
|
||||
disabledText: i18n.t('Buttons:Create new segmentation to enable this tool.'),
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation.synchronizeDrawingRadius',
|
||||
radiusOptionId: 'brush-radius',
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
],
|
||||
commands: {
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: i18n.t('Buttons:Radius (mm)'),
|
||||
id: 'brush-radius',
|
||||
type: 'range',
|
||||
explicitRunOnly: true,
|
||||
min: MIN_SEGMENTATION_DRAWING_RADIUS,
|
||||
max: MAX_SEGMENTATION_DRAWING_RADIUS,
|
||||
step: 0.5,
|
||||
value: 25,
|
||||
commands: [
|
||||
{
|
||||
commandName: 'setBrushSize',
|
||||
commandOptions: { toolNames: ['CircularBrush', 'SphereBrush'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: i18n.t('Buttons:Shape'),
|
||||
type: 'radio',
|
||||
id: 'brush-mode',
|
||||
value: 'CircularBrush',
|
||||
values: [
|
||||
{ value: 'CircularBrush', label: i18n.t('Buttons:Circle') },
|
||||
{ value: 'SphereBrush', label: i18n.t('Buttons:Sphere') },
|
||||
],
|
||||
commands: ['setToolActiveToolbar'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'InterpolateLabelmap',
|
||||
uiType: 'ohif.toolButton',
|
||||
props: {
|
||||
icon: 'actions-interpolate',
|
||||
label: i18n.t('Buttons:Interpolate Labelmap'),
|
||||
tooltip: i18n.t(
|
||||
'Buttons:Automatically fill in missing slices between drawn segments. Use brush or threshold tools on at least two slices, then click to interpolate across slices. Works in any direction. Volume must be reconstructable.'
|
||||
),
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
{
|
||||
name: 'evaluate.displaySetIsReconstructable',
|
||||
disabledText: i18n.t('Buttons:The current viewport cannot handle interpolation.'),
|
||||
},
|
||||
],
|
||||
commands: [
|
||||
{
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
},
|
||||
'interpolateLabelmap',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'SegmentBidirectional',
|
||||
uiType: 'ohif.toolButton',
|
||||
props: {
|
||||
icon: 'actions-bidirectional',
|
||||
label: i18n.t('Buttons:Segment Bidirectional'),
|
||||
tooltip: i18n.t(
|
||||
'Buttons:Automatically detects the largest length and width across slices for the selected segment and displays a bidirectional measurement.'
|
||||
),
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
disabledText: i18n.t('Buttons:Create new segmentation to enable this tool.'),
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
],
|
||||
commands: [
|
||||
{
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
},
|
||||
'runSegmentBidirectional',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'RegionSegmentPlus',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
icon: 'icon-tool-click-segment',
|
||||
label: i18n.t('Buttons:One Click Segment'),
|
||||
tooltip: i18n.t(
|
||||
'Buttons:Detects segmentable regions with one click. Hover for visual feedback—click when a plus sign appears to auto-segment the lesion.'
|
||||
),
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['RegionSegmentPlus'],
|
||||
disabledText: i18n.t('Buttons:Create new segmentation to enable this tool.'),
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
],
|
||||
commands: [
|
||||
'setToolActiveToolbar',
|
||||
{
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'LabelmapSlicePropagation',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
icon: 'icon-labelmap-slice-propagation',
|
||||
label: i18n.t('Buttons:Labelmap Assist'),
|
||||
tooltip: i18n.t(
|
||||
'Buttons:Toggle AI assistance for segmenting nearby slices. After drawing on a slice, scroll to preview predictions. Press Enter to accept or Esc to skip.'
|
||||
),
|
||||
evaluate: [
|
||||
'evaluate.cornerstoneTool.toggle',
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
],
|
||||
listeners: {
|
||||
[ViewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: callbacks(
|
||||
'LabelmapSlicePropagation'
|
||||
),
|
||||
[ViewportGridService.EVENTS.VIEWPORTS_READY]: callbacks('LabelmapSlicePropagation'),
|
||||
},
|
||||
commands: [
|
||||
{
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
},
|
||||
'toggleEnabledDisabledToolbar',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'MarkerLabelmap',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
icon: 'icon-marker-labelmap',
|
||||
label: i18n.t('Buttons:Marker Guided Labelmap'),
|
||||
tooltip: i18n.t(
|
||||
'Buttons:Use include/exclude markers to guide AI (SAM) segmentation. Click to place markers, Enter to accept results, Esc to reject, and N to go to the next slice while keeping markers.'
|
||||
),
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['MarkerLabelmap', 'MarkerInclude', 'MarkerExclude'],
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
],
|
||||
commands: [
|
||||
'setToolActiveToolbar',
|
||||
{
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
},
|
||||
],
|
||||
listeners: {
|
||||
[ViewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: callbacks('MarkerLabelmap'),
|
||||
[ViewportGridService.EVENTS.VIEWPORTS_READY]: callbacks('MarkerLabelmap'),
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: i18n.t('Buttons:Marker Mode'),
|
||||
type: 'radio',
|
||||
id: 'marker-mode',
|
||||
value: 'markerInclude',
|
||||
values: [
|
||||
{ value: 'markerInclude', label: i18n.t('Buttons:Include') },
|
||||
{ value: 'markerExclude', label: i18n.t('Buttons:Exclude') },
|
||||
],
|
||||
commands: ({ commandsManager, options }) => {
|
||||
const markerModeOption = options.find(option => option.id === 'marker-mode');
|
||||
if (markerModeOption.value === 'markerInclude') {
|
||||
commandsManager.run('setToolActive', {
|
||||
toolName: 'MarkerInclude',
|
||||
});
|
||||
} else {
|
||||
commandsManager.run('setToolActive', {
|
||||
toolName: 'MarkerExclude',
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: i18n.t('Buttons:Clear Markers'),
|
||||
type: 'button',
|
||||
id: 'clear-markers',
|
||||
commands: 'clearMarkersForMarkerLabelmap',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Eraser',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
icon: 'icon-tool-eraser',
|
||||
label: i18n.t('Buttons:Eraser'),
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['CircularEraser', 'SphereEraser'],
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation.synchronizeDrawingRadius',
|
||||
radiusOptionId: 'eraser-radius',
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: i18n.t('Buttons:Radius (mm)'),
|
||||
id: 'eraser-radius',
|
||||
type: 'range',
|
||||
explicitRunOnly: true,
|
||||
min: MIN_SEGMENTATION_DRAWING_RADIUS,
|
||||
max: MAX_SEGMENTATION_DRAWING_RADIUS,
|
||||
step: 0.5,
|
||||
value: 25,
|
||||
commands: {
|
||||
commandName: 'setBrushSize',
|
||||
commandOptions: { toolNames: ['CircularEraser', 'SphereEraser'] },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: i18n.t('Buttons:Shape'),
|
||||
type: 'radio',
|
||||
id: 'eraser-mode',
|
||||
value: 'CircularEraser',
|
||||
values: [
|
||||
{ value: 'CircularEraser', label: i18n.t('Buttons:Circle') },
|
||||
{ value: 'SphereEraser', label: i18n.t('Buttons:Sphere') },
|
||||
],
|
||||
commands: 'setToolActiveToolbar',
|
||||
},
|
||||
],
|
||||
commands: {
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Threshold',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
icon: 'icon-tool-threshold',
|
||||
label: i18n.t('Buttons:Threshold Tool'),
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: [
|
||||
'ThresholdCircularBrush',
|
||||
'ThresholdSphereBrush',
|
||||
'ThresholdCircularBrushDynamic',
|
||||
'ThresholdSphereBrushDynamic',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation.synchronizeDrawingRadius',
|
||||
radiusOptionId: 'threshold-radius',
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
],
|
||||
commands: {
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: i18n.t('Buttons:Radius (mm)'),
|
||||
id: 'threshold-radius',
|
||||
type: 'range',
|
||||
explicitRunOnly: true,
|
||||
min: MIN_SEGMENTATION_DRAWING_RADIUS,
|
||||
max: MAX_SEGMENTATION_DRAWING_RADIUS,
|
||||
step: 0.5,
|
||||
value: 25,
|
||||
commands: {
|
||||
commandName: 'setBrushSize',
|
||||
commandOptions: {
|
||||
toolNames: [
|
||||
'ThresholdCircularBrush',
|
||||
'ThresholdSphereBrush',
|
||||
'ThresholdCircularBrushDynamic',
|
||||
'ThresholdSphereBrushDynamic',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: i18n.t('Buttons:Shape'),
|
||||
type: 'radio',
|
||||
id: 'threshold-shape',
|
||||
value: 'ThresholdCircularBrush',
|
||||
values: [
|
||||
{ value: 'ThresholdCircularBrush', label: i18n.t('Buttons:Circle') },
|
||||
{ value: 'ThresholdSphereBrush', label: i18n.t('Buttons:Sphere') },
|
||||
],
|
||||
commands: ({ value, commandsManager, options }) => {
|
||||
const optionsDynamic = options.find(option => option.id === 'dynamic-mode');
|
||||
|
||||
if (optionsDynamic.value === 'ThresholdDynamic') {
|
||||
commandsManager.run('setToolActive', {
|
||||
toolName:
|
||||
value === 'ThresholdCircularBrush'
|
||||
? 'ThresholdCircularBrushDynamic'
|
||||
: 'ThresholdSphereBrushDynamic',
|
||||
});
|
||||
} else {
|
||||
commandsManager.run('setToolActive', {
|
||||
toolName: value,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: i18n.t('Buttons:Threshold'),
|
||||
type: 'radio',
|
||||
id: 'dynamic-mode',
|
||||
value: 'ThresholdDynamic',
|
||||
values: [
|
||||
{ value: 'ThresholdDynamic', label: i18n.t('Buttons:Dynamic') },
|
||||
{ value: 'ThresholdRange', label: i18n.t('Buttons:Range') },
|
||||
],
|
||||
commands: ({ value, commandsManager, options }) => {
|
||||
const thresholdRangeOption = options.find(option => option.id === 'threshold-shape');
|
||||
|
||||
if (value === 'ThresholdDynamic') {
|
||||
commandsManager.run('setToolActiveToolbar', {
|
||||
toolName:
|
||||
thresholdRangeOption.value === 'ThresholdCircularBrush'
|
||||
? 'ThresholdCircularBrushDynamic'
|
||||
: 'ThresholdSphereBrushDynamic',
|
||||
});
|
||||
} else {
|
||||
commandsManager.run('setToolActiveToolbar', {
|
||||
toolName: thresholdRangeOption.value,
|
||||
});
|
||||
|
||||
const thresholdRangeValue = options.find(
|
||||
option => option.id === 'threshold-range'
|
||||
).value;
|
||||
|
||||
commandsManager.run('setThresholdRange', {
|
||||
toolNames: ['ThresholdCircularBrush', 'ThresholdSphereBrush'],
|
||||
value: thresholdRangeValue,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'ThresholdRange',
|
||||
type: 'double-range',
|
||||
id: 'threshold-range',
|
||||
min: -1000,
|
||||
max: 1000,
|
||||
step: 1,
|
||||
value: [50, 600],
|
||||
condition: ({ options }) =>
|
||||
options.find(option => option.id === 'dynamic-mode').value === 'ThresholdRange',
|
||||
commands: {
|
||||
commandName: 'setThresholdRange',
|
||||
commandOptions: {
|
||||
toolNames: ['ThresholdCircularBrush', 'ThresholdSphereBrush'],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Shapes',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
icon: 'icon-tool-shape',
|
||||
label: i18n.t('Buttons:Shapes'),
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['CircleScissor', 'SphereScissor', 'RectangleScissor'],
|
||||
disabledText: i18n.t('Buttons:Create new segmentation to enable shapes tool.'),
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
],
|
||||
commands: {
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: i18n.t('Buttons:Shape'),
|
||||
type: 'radio',
|
||||
value: 'CircleScissor',
|
||||
id: 'shape-mode',
|
||||
values: [
|
||||
{ value: 'CircleScissor', label: i18n.t('Buttons:Circle') },
|
||||
{ value: 'SphereScissor', label: i18n.t('Buttons:Sphere') },
|
||||
{ value: 'RectangleScissor', label: i18n.t('Buttons:Rectangle') },
|
||||
],
|
||||
commands: 'setToolActiveToolbar',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'SimplifyContours',
|
||||
uiType: 'ohif.toolButton',
|
||||
props: {
|
||||
icon: 'actions-simplify',
|
||||
label: 'Simplify Contours',
|
||||
tooltip: 'Simplify Contours',
|
||||
commands: ['toggleActiveSegmentationUtility'],
|
||||
evaluate: [
|
||||
{
|
||||
name: 'cornerstone.isActiveSegmentationUtility',
|
||||
},
|
||||
],
|
||||
options: 'cornerstone.SimplifyContourOptions',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'SmoothContours',
|
||||
uiType: 'ohif.toolButton',
|
||||
props: {
|
||||
icon: 'actions-smooth',
|
||||
label: 'Smooth Contours',
|
||||
tooltip: 'Smooth Contours',
|
||||
commands: ['toggleActiveSegmentationUtility'],
|
||||
evaluate: [
|
||||
{
|
||||
name: 'cornerstone.isActiveSegmentationUtility',
|
||||
},
|
||||
],
|
||||
options: 'cornerstone.SmoothContoursOptions',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'LogicalContourOperations',
|
||||
uiType: 'ohif.toolButton',
|
||||
props: {
|
||||
icon: 'actions-combine',
|
||||
label: 'Combine Contours',
|
||||
tooltip: 'Combine Contours',
|
||||
commands: ['toggleActiveSegmentationUtility'],
|
||||
evaluate: [
|
||||
{
|
||||
name: 'cornerstone.isActiveSegmentationUtility',
|
||||
},
|
||||
],
|
||||
options: 'cornerstone.LogicalContourOperationsOptions',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'LabelMapEditWithContour',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
icon: 'tool-labelmap-edit-with-contour',
|
||||
label: i18n.t('Buttons:Labelmap Edit with Contour Tool'),
|
||||
tooltip: i18n.t('Buttons:Labelmap Edit with Contour Tool'),
|
||||
commands: [
|
||||
'setToolActiveToolbar',
|
||||
{
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: { segmentationRepresentationType: 'Labelmap' },
|
||||
},
|
||||
],
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['LabelMapEditWithContour'],
|
||||
disabledText: 'Create new segmentation to enable this tool.',
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* The toolbox / utilities section wiring for segmentation editing. These are
|
||||
* the sections rendered by the `panelSegmentationWithTools*` panels, so any
|
||||
* mode that shows those panels can merge this block into its toolbar sections.
|
||||
*/
|
||||
export const segmentationToolboxSections: Record<string, string[]> = {
|
||||
[TOOLBAR_SECTIONS.labelMapSegmentationToolbox]: ['LabelMapTools'],
|
||||
[TOOLBAR_SECTIONS.contourSegmentationToolbox]: ['ContourTools'],
|
||||
[TOOLBAR_SECTIONS.labelMapSegmentationUtilities]: ['LabelMapUtilities'],
|
||||
[TOOLBAR_SECTIONS.contourSegmentationUtilities]: ['ContourUtilities'],
|
||||
|
||||
LabelMapTools: [
|
||||
'LabelmapSlicePropagation',
|
||||
'BrushTools',
|
||||
'MarkerLabelmap',
|
||||
'RegionSegmentPlus',
|
||||
'Shapes',
|
||||
'LabelMapEditWithContour',
|
||||
],
|
||||
ContourTools: [
|
||||
'PlanarFreehandContourSegmentationTool',
|
||||
'SculptorTool',
|
||||
'SplineContourSegmentationTool',
|
||||
'LivewireContourSegmentationTool',
|
||||
],
|
||||
|
||||
LabelMapUtilities: ['InterpolateLabelmap', 'SegmentBidirectional'],
|
||||
ContourUtilities: ['LogicalContourOperations', 'SimplifyContours', 'SmoothContours'],
|
||||
|
||||
BrushTools: ['Brush', 'Eraser', 'Threshold'],
|
||||
};
|
||||
|
||||
/**
|
||||
* The segmentation mode's main toolbar layout (primary bar and viewport
|
||||
* action corners). Kept separate from the toolbox wiring above so other modes
|
||||
* can adopt segmentation editing without adopting this mode layout.
|
||||
*/
|
||||
export const segmentationModeToolbarSections: Record<string, string[]> = {
|
||||
[TOOLBAR_SECTIONS.primary]: [
|
||||
'WindowLevel',
|
||||
'Pan',
|
||||
'Zoom',
|
||||
'TrackballRotate',
|
||||
'Capture',
|
||||
'Layout',
|
||||
'Crosshairs',
|
||||
'MoreTools',
|
||||
],
|
||||
|
||||
[TOOLBAR_SECTIONS.viewportActionMenu.topLeft]: ['orientationMenu', 'dataOverlayMenu'],
|
||||
|
||||
[TOOLBAR_SECTIONS.viewportActionMenu.bottomMiddle]: ['AdvancedRenderingControls'],
|
||||
|
||||
AdvancedRenderingControls: [
|
||||
'windowLevelMenuEmbedded',
|
||||
'voiManualControlMenu',
|
||||
'Colorbar',
|
||||
'opacityMenu',
|
||||
'thresholdMenu',
|
||||
],
|
||||
|
||||
[TOOLBAR_SECTIONS.viewportActionMenu.topRight]: [
|
||||
'modalityLoadBadge',
|
||||
'trackingStatus',
|
||||
'navigationComponent',
|
||||
],
|
||||
|
||||
[TOOLBAR_SECTIONS.viewportActionMenu.bottomLeft]: ['windowLevelMenu'],
|
||||
|
||||
MoreTools: [
|
||||
'Reset',
|
||||
'rotate-right',
|
||||
'flipHorizontal',
|
||||
'ReferenceLines',
|
||||
'ImageOverlayViewer',
|
||||
'StackScroll',
|
||||
'invert',
|
||||
'Cine',
|
||||
'Magnify',
|
||||
'TagBrowser',
|
||||
],
|
||||
};
|
||||
|
||||
/**
|
||||
* Customizations registered (at default scope) by the cornerstone extension:
|
||||
* - `cornerstone.segmentationToolbarButtons` – segmentation editing button definitions
|
||||
* - `cornerstone.segmentationToolbarSections` – toolbox/utilities section wiring
|
||||
* - `cornerstone.segmentationModeToolbarSections` – the segmentation mode's toolbar layout
|
||||
* - `segmentation.*` – what the segmentation mode reads by name; lists may
|
||||
* reference other customizations, so `?customization=` modules can extend
|
||||
* them by pushing names of exported capability blocks.
|
||||
*/
|
||||
const segmentationToolbarCustomization = {
|
||||
'cornerstone.segmentationToolbarButtons': segmentationToolbarButtons,
|
||||
'cornerstone.segmentationToolbarSections': segmentationToolboxSections,
|
||||
'cornerstone.segmentationModeToolbarSections': segmentationModeToolbarSections,
|
||||
|
||||
'segmentation.toolbarButtons': [
|
||||
'cornerstone.toolbarButtons',
|
||||
'cornerstone.segmentationToolbarButtons',
|
||||
],
|
||||
'segmentation.toolbarSections': [
|
||||
'cornerstone.segmentationModeToolbarSections',
|
||||
'cornerstone.segmentationToolbarSections',
|
||||
],
|
||||
'segmentation.toolGroupAdditions': {
|
||||
default: [],
|
||||
mpr: [],
|
||||
volume3d: [],
|
||||
},
|
||||
'segmentation.leftPanels': ['@ohif/extension-default.panelModule.seriesList'],
|
||||
'segmentation.rightPanels': [
|
||||
'@ohif/extension-cornerstone.panelModule.panelSegmentationWithToolsLabelMap',
|
||||
'@ohif/extension-cornerstone.panelModule.panelSegmentationWithToolsContour',
|
||||
],
|
||||
};
|
||||
|
||||
export { segmentationToolbarButtons };
|
||||
export default segmentationToolbarCustomization;
|
||||
@@ -0,0 +1,200 @@
|
||||
import { toolNames } from '../initCornerstoneTools';
|
||||
import {
|
||||
MIN_SEGMENTATION_DRAWING_RADIUS,
|
||||
MAX_SEGMENTATION_DRAWING_RADIUS,
|
||||
} from './segmentationToolbarCustomization';
|
||||
|
||||
/**
|
||||
* Reusable tool group "capability blocks", registered as default
|
||||
* customizations so modes and `?customization=` JSON modules can add them to
|
||||
* a tool group by name via a mode's `toolGroupAdditions` customization, e.g.
|
||||
*
|
||||
* "basic.toolGroupAdditions": {
|
||||
* "default": { "$push": ["cornerstone.segmentationToolGroupTools"] }
|
||||
* }
|
||||
*
|
||||
* Each block is a `{ active/passive/enabled/disabled }` object suitable for
|
||||
* `toolGroupService.addToolsToToolGroup`.
|
||||
*/
|
||||
function getToolGroupToolsCustomization({ commandsManager }) {
|
||||
const brushInstances = [
|
||||
{
|
||||
toolName: 'CircularBrush',
|
||||
parentTool: 'Brush',
|
||||
configuration: {
|
||||
activeStrategy: 'FILL_INSIDE_CIRCLE',
|
||||
minRadius: MIN_SEGMENTATION_DRAWING_RADIUS,
|
||||
maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS,
|
||||
},
|
||||
},
|
||||
{
|
||||
toolName: 'CircularEraser',
|
||||
parentTool: 'Brush',
|
||||
configuration: {
|
||||
activeStrategy: 'ERASE_INSIDE_CIRCLE',
|
||||
minRadius: MIN_SEGMENTATION_DRAWING_RADIUS,
|
||||
maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS,
|
||||
},
|
||||
},
|
||||
{
|
||||
toolName: 'SphereBrush',
|
||||
parentTool: 'Brush',
|
||||
configuration: {
|
||||
activeStrategy: 'FILL_INSIDE_SPHERE',
|
||||
minRadius: MIN_SEGMENTATION_DRAWING_RADIUS,
|
||||
maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS,
|
||||
},
|
||||
},
|
||||
{
|
||||
toolName: 'SphereEraser',
|
||||
parentTool: 'Brush',
|
||||
configuration: {
|
||||
activeStrategy: 'ERASE_INSIDE_SPHERE',
|
||||
minRadius: MIN_SEGMENTATION_DRAWING_RADIUS,
|
||||
maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS,
|
||||
},
|
||||
},
|
||||
{
|
||||
toolName: 'ThresholdCircularBrush',
|
||||
parentTool: 'Brush',
|
||||
configuration: {
|
||||
activeStrategy: 'THRESHOLD_INSIDE_CIRCLE',
|
||||
minRadius: MIN_SEGMENTATION_DRAWING_RADIUS,
|
||||
maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS,
|
||||
},
|
||||
},
|
||||
{
|
||||
toolName: 'ThresholdSphereBrush',
|
||||
parentTool: 'Brush',
|
||||
configuration: {
|
||||
activeStrategy: 'THRESHOLD_INSIDE_SPHERE',
|
||||
minRadius: MIN_SEGMENTATION_DRAWING_RADIUS,
|
||||
maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS,
|
||||
},
|
||||
},
|
||||
{
|
||||
toolName: 'ThresholdCircularBrushDynamic',
|
||||
parentTool: 'Brush',
|
||||
configuration: {
|
||||
activeStrategy: 'THRESHOLD_INSIDE_CIRCLE',
|
||||
minRadius: MIN_SEGMENTATION_DRAWING_RADIUS,
|
||||
maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS,
|
||||
threshold: {
|
||||
isDynamic: true,
|
||||
dynamicRadius: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
toolName: 'ThresholdSphereBrushDynamic',
|
||||
parentTool: 'Brush',
|
||||
configuration: {
|
||||
activeStrategy: 'THRESHOLD_INSIDE_SPHERE',
|
||||
minRadius: MIN_SEGMENTATION_DRAWING_RADIUS,
|
||||
maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS,
|
||||
threshold: {
|
||||
isDynamic: true,
|
||||
dynamicRadius: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const splineInstances = [
|
||||
{
|
||||
toolName: 'CatmullRomSplineROI',
|
||||
parentTool: toolNames.SplineContourSegmentation,
|
||||
configuration: {
|
||||
spline: {
|
||||
type: 'CATMULLROM',
|
||||
enableTwoPointPreview: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
toolName: 'LinearSplineROI',
|
||||
parentTool: toolNames.SplineContourSegmentation,
|
||||
configuration: {
|
||||
spline: {
|
||||
type: 'LINEAR',
|
||||
enableTwoPointPreview: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
toolName: 'BSplineROI',
|
||||
parentTool: toolNames.SplineContourSegmentation,
|
||||
configuration: {
|
||||
spline: {
|
||||
type: 'BSPLINE',
|
||||
enableTwoPointPreview: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
/**
|
||||
* The segmentation editing tools (labelmap brushes/scissors and contour
|
||||
* segmentation tools), matching the buttons in
|
||||
* `cornerstone.segmentationToolbarButtons`.
|
||||
*/
|
||||
'cornerstone.segmentationToolGroupTools': {
|
||||
passive: [
|
||||
...brushInstances,
|
||||
{ toolName: toolNames.LabelmapSlicePropagation },
|
||||
{ toolName: toolNames.MarkerLabelmap },
|
||||
{ toolName: toolNames.RegionSegmentPlus },
|
||||
{ toolName: toolNames.LabelMapEditWithContourTool },
|
||||
{ toolName: toolNames.SegmentSelect },
|
||||
{ toolName: toolNames.CircleScissors },
|
||||
{ toolName: toolNames.RectangleScissors },
|
||||
{ toolName: toolNames.SphereScissors },
|
||||
{ toolName: toolNames.LivewireContourSegmentation },
|
||||
{ toolName: toolNames.SculptorTool },
|
||||
...splineInstances,
|
||||
],
|
||||
},
|
||||
|
||||
/**
|
||||
* The measurement/annotation tools, matching the `MeasurementTools`
|
||||
* buttons in `cornerstone.toolbarButtons`. Useful for adding annotations
|
||||
* to modes (such as segmentation) whose tool groups omit them.
|
||||
*/
|
||||
'cornerstone.annotationToolGroupTools': {
|
||||
passive: [
|
||||
{ toolName: toolNames.Length },
|
||||
{
|
||||
toolName: toolNames.ArrowAnnotate,
|
||||
configuration: {
|
||||
getTextCallback: (callback, eventDetails) => {
|
||||
commandsManager.runCommand('arrowTextCallback', {
|
||||
callback,
|
||||
eventDetails,
|
||||
});
|
||||
},
|
||||
changeTextCallback: (data, eventDetails, callback) => {
|
||||
commandsManager.runCommand('arrowTextCallback', {
|
||||
callback,
|
||||
data,
|
||||
eventDetails,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
{ toolName: toolNames.Bidirectional },
|
||||
{ toolName: toolNames.Probe },
|
||||
{ toolName: toolNames.DragProbe },
|
||||
{ toolName: toolNames.EllipticalROI },
|
||||
{ toolName: toolNames.CircleROI },
|
||||
{ toolName: toolNames.RectangleROI },
|
||||
{ toolName: toolNames.Angle },
|
||||
{ toolName: toolNames.CobbAngle },
|
||||
{ toolName: toolNames.SplineROI },
|
||||
{ toolName: toolNames.LivewireContour },
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default getToolGroupToolsCustomization;
|
||||
@@ -794,6 +794,27 @@ export const toolbarSections = {
|
||||
const toolbarButtonsCustomization = {
|
||||
'cornerstone.toolbarButtons': toolbarButtons,
|
||||
'cornerstone.toolbarSections': toolbarSections,
|
||||
|
||||
// What the basic (and longitudinal) mode reads by name. The list values may
|
||||
// reference other customizations, so a `?customization=` module can extend a
|
||||
// mode with an exported capability block by pushing its name, e.g.
|
||||
// `"basic.toolbarButtons": { "$push": ["cornerstone.segmentationToolbarButtons"] }`.
|
||||
'basic.toolbarButtons': ['cornerstone.toolbarButtons'],
|
||||
'basic.toolbarSections': ['cornerstone.toolbarSections'],
|
||||
// Extra tools layered onto the mode's tool groups after creation; entries
|
||||
// reference tool blocks such as `cornerstone.segmentationToolGroupTools`.
|
||||
'basic.toolGroupAdditions': {
|
||||
default: [],
|
||||
mpr: [],
|
||||
SRToolGroup: [],
|
||||
volume3d: [],
|
||||
},
|
||||
// Panel lists resolved by the basic mode's layout template.
|
||||
'basic.leftPanels': ['@ohif/extension-default.panelModule.seriesList'],
|
||||
'basic.rightPanels': [
|
||||
'@ohif/extension-cornerstone.panelModule.panelSegmentation',
|
||||
'@ohif/extension-cornerstone.panelModule.panelMeasurement',
|
||||
],
|
||||
};
|
||||
|
||||
export { toolbarButtons };
|
||||
|
||||
@@ -9,6 +9,8 @@ import colorbarCustomization from './customizations/colorbarCustomization';
|
||||
import modalityColorMapCustomization from './customizations/modalityColorMapCustomization';
|
||||
import windowLevelPresetsCustomization from './customizations/windowLevelPresetsCustomization';
|
||||
import toolbarButtonsCustomization from './customizations/toolbarButtonsCustomization';
|
||||
import segmentationToolbarCustomization from './customizations/segmentationToolbarCustomization';
|
||||
import getToolGroupToolsCustomization from './customizations/toolGroupToolsCustomization';
|
||||
import miscCustomization from './customizations/miscCustomization';
|
||||
import captureViewportModalCustomization from './customizations/captureViewportModalCustomization';
|
||||
import viewportDownloadWarningCustomization from './customizations/viewportDownloadWarningCustomization';
|
||||
@@ -34,6 +36,8 @@ function getCustomizationModule({ commandsManager, servicesManager, extensionMan
|
||||
...modalityColorMapCustomization,
|
||||
...windowLevelPresetsCustomization,
|
||||
...toolbarButtonsCustomization,
|
||||
...segmentationToolbarCustomization,
|
||||
...getToolGroupToolsCustomization({ commandsManager }),
|
||||
...miscCustomization,
|
||||
...captureViewportModalCustomization,
|
||||
...viewportDownloadWarningCustomization,
|
||||
|
||||
@@ -4,7 +4,20 @@ export default function getCustomizationModule() {
|
||||
return [
|
||||
{
|
||||
name: 'default',
|
||||
value: { ...measurementTrackingPrompts },
|
||||
value: {
|
||||
...measurementTrackingPrompts,
|
||||
|
||||
// Panel lists resolved by the longitudinal mode's layout template;
|
||||
// `?customization=` modules can replace them (e.g. to swap in the
|
||||
// segmentation panels with editing tools).
|
||||
'longitudinal.leftPanels': [
|
||||
'@ohif/extension-measurement-tracking.panelModule.seriesList',
|
||||
],
|
||||
'longitudinal.rightPanels': [
|
||||
'@ohif/extension-cornerstone.panelModule.panelSegmentation',
|
||||
'@ohif/extension-measurement-tracking.panelModule.trackedMeasurements',
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,535 @@
|
||||
import { ToolbarService } from '@ohif/core';
|
||||
import i18n from 'i18next';
|
||||
|
||||
import { toolGroupIds } from '../toolGroupIds';
|
||||
|
||||
const { TOOLBAR_SECTIONS } = ToolbarService;
|
||||
|
||||
export const MIN_SEGMENTATION_DRAWING_RADIUS = 0.5;
|
||||
export const MAX_SEGMENTATION_DRAWING_RADIUS = 99.5;
|
||||
|
||||
const setToolActiveToolbar = {
|
||||
commandName: 'setToolActiveToolbar',
|
||||
commandOptions: {
|
||||
toolGroupIds: [toolGroupIds.CT, toolGroupIds.PT, toolGroupIds.Fusion],
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Default toolbar buttons for the TMTV mode, registered as the
|
||||
* `tmtv.toolbarButtons` customization so `?customization=` modules can extend
|
||||
* or replace them.
|
||||
*/
|
||||
const toolbarButtons = [
|
||||
{
|
||||
id: 'MeasurementTools',
|
||||
uiType: 'ohif.toolButtonList',
|
||||
props: {
|
||||
buttonSection: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'SegmentationTools',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
buttonSection: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'BrushTools',
|
||||
uiType: 'ohif.toolBoxButtonGroup',
|
||||
props: {
|
||||
buttonSection: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'AdvancedRenderingControls',
|
||||
uiType: 'ohif.advancedRenderingControls',
|
||||
props: {
|
||||
buttonSection: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'modalityLoadBadge',
|
||||
uiType: 'ohif.modalityLoadBadge',
|
||||
props: {
|
||||
icon: 'Status',
|
||||
label: i18n.t('Buttons:Status'),
|
||||
tooltip: i18n.t('Buttons:Status'),
|
||||
evaluate: {
|
||||
name: 'evaluate.modalityLoadBadge',
|
||||
hideWhenDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Colorbar',
|
||||
uiType: 'ohif.colorbar',
|
||||
props: {
|
||||
type: 'tool',
|
||||
label: i18n.t('Buttons:Colorbar'),
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'navigationComponent',
|
||||
uiType: 'ohif.navigationComponent',
|
||||
props: {
|
||||
icon: 'Navigation',
|
||||
label: i18n.t('Buttons:Navigation'),
|
||||
tooltip: i18n.t('Buttons:Navigate between segments/measurements and manage their visibility'),
|
||||
evaluate: {
|
||||
name: 'evaluate.navigationComponent',
|
||||
hideWhenDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'windowLevelMenuEmbedded',
|
||||
uiType: 'ohif.windowLevelMenuEmbedded',
|
||||
props: {
|
||||
icon: 'WindowLevel',
|
||||
label: i18n.t('Buttons:Window Level'),
|
||||
tooltip: i18n.t('Buttons:Adjust window/level presets and customize image contrast settings'),
|
||||
evaluate: {
|
||||
name: 'evaluate.windowLevelMenuEmbedded',
|
||||
hideWhenDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'trackingStatus',
|
||||
uiType: 'ohif.trackingStatus',
|
||||
props: {
|
||||
icon: 'TrackingStatus',
|
||||
label: i18n.t('Buttons:Tracking Status'),
|
||||
tooltip: i18n.t('Buttons:View and manage tracking status of measurements and annotations'),
|
||||
evaluate: {
|
||||
name: 'evaluate.trackingStatus',
|
||||
hideWhenDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Length',
|
||||
uiType: 'ohif.toolButton',
|
||||
props: {
|
||||
icon: 'tool-length',
|
||||
label: i18n.t('Buttons:Length'),
|
||||
tooltip: i18n.t('Buttons:Length Tool'),
|
||||
commands: setToolActiveToolbar,
|
||||
evaluate: 'evaluate.cornerstoneTool',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Bidirectional',
|
||||
uiType: 'ohif.toolButton',
|
||||
props: {
|
||||
icon: 'tool-bidirectional',
|
||||
label: i18n.t('Buttons:Bidirectional'),
|
||||
tooltip: i18n.t('Buttons:Bidirectional Tool'),
|
||||
commands: setToolActiveToolbar,
|
||||
evaluate: 'evaluate.cornerstoneTool',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'ArrowAnnotate',
|
||||
uiType: 'ohif.toolButton',
|
||||
props: {
|
||||
icon: 'tool-annotate',
|
||||
label: i18n.t('Buttons:Arrow Annotate'),
|
||||
tooltip: i18n.t('Buttons:Arrow Annotate Tool'),
|
||||
commands: setToolActiveToolbar,
|
||||
evaluate: 'evaluate.cornerstoneTool',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'EllipticalROI',
|
||||
uiType: 'ohif.toolButton',
|
||||
props: {
|
||||
icon: 'tool-ellipse',
|
||||
label: i18n.t('Buttons:Ellipse'),
|
||||
tooltip: i18n.t('Buttons:Ellipse Tool'),
|
||||
commands: setToolActiveToolbar,
|
||||
evaluate: 'evaluate.cornerstoneTool',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Zoom',
|
||||
uiType: 'ohif.toolButton',
|
||||
props: {
|
||||
icon: 'tool-zoom',
|
||||
label: i18n.t('Buttons:Zoom'),
|
||||
commands: setToolActiveToolbar,
|
||||
evaluate: 'evaluate.cornerstoneTool',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'WindowLevel',
|
||||
uiType: 'ohif.toolButton',
|
||||
props: {
|
||||
icon: 'tool-window-level',
|
||||
label: i18n.t('Buttons:Window Level'),
|
||||
commands: setToolActiveToolbar,
|
||||
evaluate: 'evaluate.cornerstoneTool',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Crosshairs',
|
||||
uiType: 'ohif.toolButton',
|
||||
props: {
|
||||
icon: 'tool-crosshair',
|
||||
label: i18n.t('Buttons:Crosshairs'),
|
||||
commands: setToolActiveToolbar,
|
||||
evaluate: 'evaluate.cornerstoneTool',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Pan',
|
||||
uiType: 'ohif.toolButton',
|
||||
props: {
|
||||
icon: 'tool-move',
|
||||
label: i18n.t('Buttons:Pan'),
|
||||
commands: setToolActiveToolbar,
|
||||
evaluate: 'evaluate.cornerstoneTool',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'RectangleROIStartEndThreshold',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
icon: 'tool-create-threshold',
|
||||
label: i18n.t('Buttons:Rectangle ROI Threshold'),
|
||||
commands: setToolActiveToolbar,
|
||||
evaluate: [
|
||||
'evaluate.cornerstone.segmentation',
|
||||
{
|
||||
name: 'evaluate.cornerstoneTool',
|
||||
disabledText: i18n.t('Buttons:Select the PT Axial to enable this tool'),
|
||||
},
|
||||
],
|
||||
options: 'tmtv.RectangleROIThresholdOptions',
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
id: 'Brush',
|
||||
uiType: 'ohif.toolButton',
|
||||
props: {
|
||||
icon: 'icon-tool-brush',
|
||||
label: i18n.t('Buttons:Brush'),
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['CircularBrush', 'SphereBrush'],
|
||||
disabledText: i18n.t('Buttons:Create new segmentation to enable this tool.'),
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation.synchronizeDrawingRadius',
|
||||
radiusOptionId: 'brush-radius',
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: i18n.t('Buttons:Radius (mm)'),
|
||||
id: 'brush-radius',
|
||||
type: 'range',
|
||||
explicitRunOnly: true,
|
||||
min: MIN_SEGMENTATION_DRAWING_RADIUS,
|
||||
max: MAX_SEGMENTATION_DRAWING_RADIUS,
|
||||
step: 0.5,
|
||||
value: 25,
|
||||
commands: {
|
||||
commandName: 'setBrushSize',
|
||||
commandOptions: { toolNames: ['CircularBrush', 'SphereBrush'] },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: i18n.t('Buttons:Shape'),
|
||||
type: 'radio',
|
||||
id: 'brush-mode',
|
||||
value: 'CircularBrush',
|
||||
values: [
|
||||
{ value: 'CircularBrush', label: i18n.t('Buttons:Circle') },
|
||||
{ value: 'SphereBrush', label: i18n.t('Buttons:Sphere') },
|
||||
],
|
||||
commands: 'setToolActiveToolbar',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Eraser',
|
||||
uiType: 'ohif.toolButton',
|
||||
props: {
|
||||
icon: 'icon-tool-eraser',
|
||||
label: i18n.t('Buttons:Eraser'),
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['CircularEraser', 'SphereEraser'],
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation.synchronizeDrawingRadius',
|
||||
radiusOptionId: 'eraser-radius',
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: i18n.t('Buttons:Radius (mm)'),
|
||||
id: 'eraser-radius',
|
||||
type: 'range',
|
||||
explicitRunOnly: true,
|
||||
min: MIN_SEGMENTATION_DRAWING_RADIUS,
|
||||
max: MAX_SEGMENTATION_DRAWING_RADIUS,
|
||||
step: 0.5,
|
||||
value: 25,
|
||||
commands: {
|
||||
commandName: 'setBrushSize',
|
||||
commandOptions: { toolNames: ['CircularEraser', 'SphereEraser'] },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: i18n.t('Buttons:Shape'),
|
||||
type: 'radio',
|
||||
id: 'eraser-mode',
|
||||
value: 'CircularEraser',
|
||||
values: [
|
||||
{ value: 'CircularEraser', label: i18n.t('Buttons:Circle') },
|
||||
{ value: 'SphereEraser', label: i18n.t('Buttons:Sphere') },
|
||||
],
|
||||
commands: 'setToolActiveToolbar',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Threshold',
|
||||
uiType: 'ohif.toolButton',
|
||||
props: {
|
||||
icon: 'icon-tool-threshold',
|
||||
label: i18n.t('Buttons:Threshold Tool'),
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: [
|
||||
'ThresholdCircularBrush',
|
||||
'ThresholdSphereBrush',
|
||||
'ThresholdCircularBrushDynamic',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation.synchronizeDrawingRadius',
|
||||
radiusOptionId: 'threshold-radius',
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: i18n.t('Buttons:Radius (mm)'),
|
||||
id: 'threshold-radius',
|
||||
type: 'range',
|
||||
explicitRunOnly: true,
|
||||
min: MIN_SEGMENTATION_DRAWING_RADIUS,
|
||||
max: MAX_SEGMENTATION_DRAWING_RADIUS,
|
||||
step: 0.5,
|
||||
value: 25,
|
||||
commands: {
|
||||
commandName: 'setBrushSize',
|
||||
commandOptions: {
|
||||
toolNames: [
|
||||
'ThresholdCircularBrush',
|
||||
'ThresholdSphereBrush',
|
||||
'ThresholdCircularBrushDynamic',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: i18n.t('Buttons:Threshold'),
|
||||
type: 'radio',
|
||||
id: 'dynamic-mode',
|
||||
value: 'ThresholdRange',
|
||||
values: [
|
||||
{ value: 'ThresholdDynamic', label: i18n.t('Buttons:Dynamic') },
|
||||
{ value: 'ThresholdRange', label: i18n.t('Buttons:Range') },
|
||||
],
|
||||
commands: ({ value, commandsManager }) => {
|
||||
if (value === 'ThresholdDynamic') {
|
||||
commandsManager.run('setToolActive', {
|
||||
toolName: 'ThresholdCircularBrushDynamic',
|
||||
});
|
||||
} else {
|
||||
commandsManager.run('setToolActive', {
|
||||
toolName: 'ThresholdCircularBrush',
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: i18n.t('Buttons:Shape'),
|
||||
type: 'radio',
|
||||
id: 'eraser-mode',
|
||||
value: 'ThresholdCircularBrush',
|
||||
values: [
|
||||
{ value: 'ThresholdCircularBrush', label: i18n.t('Buttons:Circle') },
|
||||
{ value: 'ThresholdSphereBrush', label: i18n.t('Buttons:Sphere') },
|
||||
],
|
||||
condition: ({ options }) =>
|
||||
options.find(option => option.id === 'dynamic-mode').value === 'ThresholdRange',
|
||||
commands: 'setToolActiveToolbar',
|
||||
},
|
||||
{
|
||||
name: i18n.t('ROIThresholdConfiguration:ThresholdRange'),
|
||||
type: 'double-range',
|
||||
id: 'threshold-range',
|
||||
min: 0,
|
||||
max: 50,
|
||||
step: 0.5,
|
||||
value: [2.5, 50],
|
||||
condition: ({ options }) =>
|
||||
options.find(option => option.id === 'dynamic-mode').value === 'ThresholdRange',
|
||||
commands: {
|
||||
commandName: 'setThresholdRange',
|
||||
commandOptions: {
|
||||
toolNames: ['ThresholdCircularBrush', 'ThresholdSphereBrush'],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'dataOverlayMenu',
|
||||
uiType: 'ohif.dataOverlayMenu',
|
||||
props: {
|
||||
icon: 'ViewportViews',
|
||||
label: i18n.t('Buttons:Data Overlay'),
|
||||
tooltip: i18n.t(
|
||||
'Buttons:Configure data overlay options and manage foreground/background display sets'
|
||||
),
|
||||
evaluate: 'evaluate.dataOverlayMenu',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'orientationMenu',
|
||||
uiType: 'ohif.orientationMenu',
|
||||
props: {
|
||||
icon: 'OrientationSwitch',
|
||||
label: i18n.t('Buttons:Orientation'),
|
||||
tooltip: i18n.t(
|
||||
'Buttons:Change viewport orientation between axial, sagittal, coronal and reformat planes'
|
||||
),
|
||||
evaluate: {
|
||||
name: 'evaluate.orientationMenu',
|
||||
// hideWhenDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'windowLevelMenu',
|
||||
uiType: 'ohif.windowLevelMenu',
|
||||
props: {
|
||||
icon: 'WindowLevel',
|
||||
label: i18n.t('Buttons:Window Level'),
|
||||
tooltip: i18n.t('Buttons:Adjust window/level presets and customize image contrast settings'),
|
||||
evaluate: 'evaluate.windowLevelMenu',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'voiManualControlMenu',
|
||||
uiType: 'ohif.voiManualControlMenu',
|
||||
props: {
|
||||
icon: 'WindowLevelAdvanced',
|
||||
label: i18n.t('Buttons:Advanced Window Level'),
|
||||
tooltip: i18n.t('Buttons:Advanced window/level settings with manual controls and presets'),
|
||||
evaluate: 'evaluate.voiManualControlMenu',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'thresholdMenu',
|
||||
uiType: 'ohif.thresholdMenu',
|
||||
props: {
|
||||
icon: 'Threshold',
|
||||
label: i18n.t('Buttons:Threshold'),
|
||||
tooltip: i18n.t('Buttons:Image threshold settings'),
|
||||
evaluate: {
|
||||
name: 'evaluate.thresholdMenu',
|
||||
hideWhenDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'opacityMenu',
|
||||
uiType: 'ohif.opacityMenu',
|
||||
props: {
|
||||
icon: 'Opacity',
|
||||
label: i18n.t('Buttons:Opacity'),
|
||||
tooltip: i18n.t('Buttons:Image opacity settings'),
|
||||
evaluate: {
|
||||
name: 'evaluate.opacityMenu',
|
||||
hideWhenDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Default toolbar layout for the TMTV mode, registered as the
|
||||
* `tmtv.toolbarSections` customization.
|
||||
*/
|
||||
export const toolbarSections: Record<string, string[]> = {
|
||||
[TOOLBAR_SECTIONS.primary]: ['MeasurementTools', 'Zoom', 'Pan', 'WindowLevel', 'Crosshairs'],
|
||||
|
||||
[TOOLBAR_SECTIONS.viewportActionMenu.topLeft]: ['orientationMenu', 'dataOverlayMenu'],
|
||||
|
||||
[TOOLBAR_SECTIONS.viewportActionMenu.bottomMiddle]: ['AdvancedRenderingControls'],
|
||||
|
||||
AdvancedRenderingControls: [
|
||||
'windowLevelMenuEmbedded',
|
||||
'voiManualControlMenu',
|
||||
'Colorbar',
|
||||
'opacityMenu',
|
||||
'thresholdMenu',
|
||||
],
|
||||
|
||||
[TOOLBAR_SECTIONS.viewportActionMenu.topRight]: [
|
||||
'modalityLoadBadge',
|
||||
'trackingStatus',
|
||||
'navigationComponent',
|
||||
],
|
||||
|
||||
[TOOLBAR_SECTIONS.viewportActionMenu.bottomLeft]: ['windowLevelMenu'],
|
||||
|
||||
MeasurementTools: ['Length', 'Bidirectional', 'ArrowAnnotate', 'EllipticalROI'],
|
||||
|
||||
ROIThresholdToolbox: ['SegmentationTools'],
|
||||
SegmentationTools: ['RectangleROIStartEndThreshold', 'BrushTools'],
|
||||
BrushTools: ['Brush', 'Eraser', 'Threshold'],
|
||||
};
|
||||
|
||||
/**
|
||||
* Customizations registered (at default scope) by the tmtv extension. The
|
||||
* TMTV mode reads these by name so `?customization=` modules can extend the
|
||||
* defaults, and site modes can extend the TMTV mode with different names.
|
||||
*/
|
||||
const toolbarCustomization = {
|
||||
'tmtv.toolbarButtons': toolbarButtons,
|
||||
'tmtv.toolbarSections': toolbarSections,
|
||||
// Extra tools layered onto the mode's tool groups after creation; entries
|
||||
// reference tool blocks such as `cornerstone.annotationToolGroupTools`.
|
||||
'tmtv.toolGroupAdditions': {
|
||||
[toolGroupIds.CT]: [],
|
||||
[toolGroupIds.PT]: [],
|
||||
[toolGroupIds.Fusion]: [],
|
||||
[toolGroupIds.MIP]: [],
|
||||
[toolGroupIds.default]: [],
|
||||
},
|
||||
// Panel lists resolved by the TMTV mode's layout template.
|
||||
'tmtv.leftPanels': ['@ohif/extension-default.panelModule.seriesList'],
|
||||
'tmtv.rightPanels': [
|
||||
'@ohif/extension-tmtv.panelModule.tmtv',
|
||||
'@ohif/extension-tmtv.panelModule.petSUV',
|
||||
],
|
||||
};
|
||||
|
||||
export { toolbarButtons };
|
||||
export default toolbarCustomization;
|
||||
@@ -0,0 +1,17 @@
|
||||
import toolbarCustomization from './customizations/toolbarCustomization';
|
||||
|
||||
/**
|
||||
* Registers the TMTV defaults (toolbar buttons/sections, tool group
|
||||
* additions and panel lists) so the TMTV mode can reference them by name and
|
||||
* `?customization=` modules can extend them.
|
||||
*/
|
||||
export default function getCustomizationModule() {
|
||||
return [
|
||||
{
|
||||
name: 'default',
|
||||
value: {
|
||||
...toolbarCustomization,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import getPanelModule from './getPanelModule';
|
||||
import init from './init';
|
||||
import commandsModule from './commandsModule';
|
||||
import getToolbarModule from './getToolbarModule';
|
||||
import getCustomizationModule from './getCustomizationModule';
|
||||
import { toolGroupIds } from './toolGroupIds';
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -19,6 +21,7 @@ const tmtvExtension = {
|
||||
getToolbarModule,
|
||||
getPanelModule,
|
||||
getHangingProtocolModule,
|
||||
getCustomizationModule,
|
||||
getCommandsModule({ servicesManager, commandsManager, extensionManager }) {
|
||||
return commandsModule({
|
||||
servicesManager,
|
||||
@@ -29,3 +32,4 @@ const tmtvExtension = {
|
||||
};
|
||||
|
||||
export default tmtvExtension;
|
||||
export { toolGroupIds };
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* The tool group ids used by the TMTV hanging protocols and mode. Defined in
|
||||
* the extension so both the extension (hanging protocol viewports, toolbar
|
||||
* buttons) and the mode (tool group creation) share one definition.
|
||||
*/
|
||||
export const toolGroupIds = {
|
||||
CT: 'ctToolGroup',
|
||||
PT: 'ptToolGroup',
|
||||
Fusion: 'fusionToolGroup',
|
||||
MIP: 'mipToolGroup',
|
||||
default: 'default',
|
||||
};
|
||||
|
||||
export default toolGroupIds;
|
||||
Reference in new issue
Block a user