feature(segmentation): Add contour tools and utilities in a side panel (#5517)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Dan Rukas <dan.rukas@gmail.com>
This commit is contained in:
73 files changed
+2770
-435
No files matched your search
@@ -659,14 +659,6 @@ const toolbarButtons: Button[] = [
|
||||
},
|
||||
},
|
||||
},
|
||||
// Section containers for the nested toolbox
|
||||
{
|
||||
id: 'SegmentationUtilities',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
buttonSection: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'SegmentLabelTool',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
|
||||
@@ -71,11 +71,11 @@ const config = {
|
||||
name: 'preset-default',
|
||||
params: {
|
||||
overrides: {
|
||||
removeViewBox: false
|
||||
removeViewBox: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
],
|
||||
},
|
||||
prettier: false,
|
||||
svgo: true,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { id } from './id';
|
||||
import toolbarButtons from './toolbarButtons';
|
||||
import initToolGroups from './initToolGroups';
|
||||
import setUpAutoTabSwitchHandler from './utils/setUpAutoTabSwitchHandler';
|
||||
|
||||
const ohif = {
|
||||
layout: '@ohif/extension-default.layoutTemplateModule.viewerLayout',
|
||||
@@ -11,7 +12,10 @@ const ohif = {
|
||||
|
||||
const cornerstone = {
|
||||
viewport: '@ohif/extension-cornerstone.viewportModule.cornerstone',
|
||||
panelTool: '@ohif/extension-cornerstone.panelModule.panelSegmentationWithTools',
|
||||
labelMapSegmentationPanel:
|
||||
'@ohif/extension-cornerstone.panelModule.panelSegmentationWithToolsLabelMap',
|
||||
contourSegmentationPanel:
|
||||
'@ohif/extension-cornerstone.panelModule.panelSegmentationWithToolsContour',
|
||||
measurements: '@ohif/extension-cornerstone.panelModule.panelMeasurement',
|
||||
};
|
||||
|
||||
@@ -36,6 +40,7 @@ const extensionDependencies = {
|
||||
};
|
||||
|
||||
function modeFactory({ modeConfiguration }) {
|
||||
const _unsubscriptions = [];
|
||||
return {
|
||||
/**
|
||||
* Mode ID, which should be unique among modes used by the viewer. This ID
|
||||
@@ -53,8 +58,14 @@ function modeFactory({ modeConfiguration }) {
|
||||
* Services and other resources.
|
||||
*/
|
||||
onModeEnter: ({ servicesManager, extensionManager, commandsManager }: withAppTypes) => {
|
||||
const { measurementService, toolbarService, toolGroupService, customizationService } =
|
||||
servicesManager.services;
|
||||
const {
|
||||
measurementService,
|
||||
toolbarService,
|
||||
toolGroupService,
|
||||
segmentationService,
|
||||
viewportGridService,
|
||||
panelService,
|
||||
} = servicesManager.services;
|
||||
|
||||
measurementService.clearMeasurements();
|
||||
|
||||
@@ -114,22 +125,54 @@ function modeFactory({ modeConfiguration }) {
|
||||
'TagBrowser',
|
||||
]);
|
||||
|
||||
toolbarService.updateSection(toolbarService.sections.segmentationToolbox, [
|
||||
'SegmentationUtilities',
|
||||
'SegmentationTools',
|
||||
toolbarService.updateSection(toolbarService.sections.labelMapSegmentationToolbox, [
|
||||
'LabelMapTools',
|
||||
]);
|
||||
toolbarService.updateSection('SegmentationUtilities', [
|
||||
toolbarService.updateSection(toolbarService.sections.contourSegmentationToolbox, [
|
||||
'ContourTools',
|
||||
]);
|
||||
|
||||
toolbarService.updateSection('LabelMapTools', [
|
||||
'LabelmapSlicePropagation',
|
||||
'InterpolateLabelmap',
|
||||
'SegmentBidirectional',
|
||||
]);
|
||||
toolbarService.updateSection('SegmentationTools', [
|
||||
'BrushTools',
|
||||
'MarkerLabelmap',
|
||||
'RegionSegmentPlus',
|
||||
'Shapes',
|
||||
'LabelMapEditWithContour',
|
||||
]);
|
||||
toolbarService.updateSection('ContourTools', [
|
||||
'PlanarFreehandContourSegmentationTool',
|
||||
'SculptorTool',
|
||||
'SplineContourSegmentationTool',
|
||||
'LivewireContourSegmentationTool',
|
||||
]);
|
||||
|
||||
toolbarService.updateSection(toolbarService.sections.labelMapSegmentationUtilities, [
|
||||
'LabelMapUtilities',
|
||||
]);
|
||||
toolbarService.updateSection(toolbarService.sections.contourSegmentationUtilities, [
|
||||
'ContourUtilities',
|
||||
]);
|
||||
|
||||
toolbarService.updateSection('LabelMapUtilities', [
|
||||
'InterpolateLabelmap',
|
||||
'SegmentBidirectional',
|
||||
]);
|
||||
toolbarService.updateSection('ContourUtilities', [
|
||||
'LogicalContourOperations',
|
||||
'SimplifyContours',
|
||||
'SmoothContours',
|
||||
]);
|
||||
|
||||
toolbarService.updateSection('BrushTools', ['Brush', 'Eraser', 'Threshold']);
|
||||
|
||||
const { unsubscribeAutoTabSwitchEvents } = setUpAutoTabSwitchHandler({
|
||||
segmentationService,
|
||||
viewportGridService,
|
||||
panelService,
|
||||
});
|
||||
|
||||
_unsubscriptions.push(...unsubscribeAutoTabSwitchEvents);
|
||||
},
|
||||
onModeExit: ({ servicesManager }: withAppTypes) => {
|
||||
const {
|
||||
@@ -141,6 +184,9 @@ function modeFactory({ modeConfiguration }) {
|
||||
uiModalService,
|
||||
} = servicesManager.services;
|
||||
|
||||
_unsubscriptions.forEach(unsubscribe => unsubscribe());
|
||||
_unsubscriptions.length = 0;
|
||||
|
||||
uiDialogService.hideAll();
|
||||
uiModalService.hide();
|
||||
toolGroupService.destroy();
|
||||
@@ -192,7 +238,10 @@ function modeFactory({ modeConfiguration }) {
|
||||
props: {
|
||||
leftPanels: [ohif.leftPanel],
|
||||
leftPanelResizable: true,
|
||||
rightPanels: [cornerstone.panelTool],
|
||||
rightPanels: [
|
||||
cornerstone.contourSegmentationPanel,
|
||||
cornerstone.labelMapSegmentationPanel,
|
||||
],
|
||||
rightPanelResizable: true,
|
||||
// leftPanelClosed: true,
|
||||
viewports: [
|
||||
|
||||
@@ -106,6 +106,9 @@ function createTools({ utilityModule, commandsManager }) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
toolName: toolNames.LabelMapEditWithContourTool,
|
||||
},
|
||||
{ toolName: toolNames.CircleScissors },
|
||||
{ toolName: toolNames.RectangleScissors },
|
||||
{ toolName: toolNames.SphereScissors },
|
||||
@@ -114,6 +117,42 @@ function createTools({ utilityModule, commandsManager }) {
|
||||
{ toolName: toolNames.WindowLevelRegion },
|
||||
|
||||
{ toolName: toolNames.UltrasoundDirectional },
|
||||
{
|
||||
toolName: toolNames.PlanarFreehandContourSegmentation,
|
||||
},
|
||||
{ toolName: toolNames.LivewireContourSegmentation },
|
||||
{ toolName: toolNames.SculptorTool },
|
||||
{ toolName: toolNames.PlanarFreehandROI },
|
||||
{
|
||||
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,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
disabled: [{ toolName: toolNames.ReferenceLines }, { toolName: toolNames.AdvancedMagnify }],
|
||||
};
|
||||
|
||||
@@ -174,17 +174,31 @@ const toolbarButtons: Button[] = [
|
||||
buttonSection: true,
|
||||
},
|
||||
},
|
||||
// Section containers for the nested toolbox
|
||||
// Section containers for the nested toolboxes and toolbars.
|
||||
{
|
||||
id: 'SegmentationUtilities',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
id: 'LabelMapUtilities',
|
||||
uiType: 'ohif.Toolbar',
|
||||
props: {
|
||||
buttonSection: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'SegmentationTools',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
id: 'ContourUtilities',
|
||||
uiType: 'ohif.Toolbar',
|
||||
props: {
|
||||
buttonSection: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'LabelMapTools',
|
||||
uiType: 'ohif.toolBoxButtonGroup',
|
||||
props: {
|
||||
buttonSection: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'ContourTools',
|
||||
uiType: 'ohif.toolBoxButtonGroup',
|
||||
props: {
|
||||
buttonSection: true,
|
||||
},
|
||||
@@ -399,17 +413,254 @@ const toolbarButtons: Button[] = [
|
||||
commands: 'openDICOMTagViewer',
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
id: 'PlanarFreehandContourSegmentationTool',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
icon: 'icon-tool-freehand-roi',
|
||||
label: 'Freehand Segmentation',
|
||||
tooltip: 'Freehand Segmentation',
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['PlanarFreehandContourSegmentationTool'],
|
||||
disabledText: '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: 'Interpolate Contours',
|
||||
type: 'switch',
|
||||
id: 'planarFreehandInterpolateContours',
|
||||
value: false,
|
||||
commands: {
|
||||
commandName: 'setInterpolationToolConfiguration',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'LivewireContourSegmentationTool',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
icon: 'icon-tool-livewire',
|
||||
label: 'Livewire Contour',
|
||||
tooltip: 'Livewire Contour',
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['LivewireContourSegmentationTool'],
|
||||
disabledText: '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: 'Interpolate Contours',
|
||||
type: 'switch',
|
||||
id: 'livewireInterpolateContours',
|
||||
value: false,
|
||||
commands: {
|
||||
commandName: 'setInterpolationToolConfiguration',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'SplineContourSegmentationTool',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
props: {
|
||||
icon: 'icon-tool-spline-roi',
|
||||
label: 'Spline Contour Segmentation Tool',
|
||||
tooltip: 'Spline Contour Segmentation Tool',
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['CatmullRomSplineROI', 'LinearSplineROI', 'BSplineROI'],
|
||||
disabledText: 'Create new segmentation to enable this tool.',
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Contour',
|
||||
},
|
||||
],
|
||||
commands: [
|
||||
{
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Contour',
|
||||
},
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: 'Spline Type',
|
||||
type: 'select',
|
||||
id: 'splineTypeSelect',
|
||||
value: 'CatmullRomSplineROI',
|
||||
values: [
|
||||
{
|
||||
id: 'CatmullRomSplineROI',
|
||||
value: 'CatmullRomSplineROI',
|
||||
label: 'Catmull Rom Spline',
|
||||
},
|
||||
{ id: 'LinearSplineROI', value: 'LinearSplineROI', label: 'Linear Spline' },
|
||||
{ id: 'BSplineROI', value: 'BSplineROI', label: 'B-Spline' },
|
||||
],
|
||||
commands: {
|
||||
commandName: 'setToolActiveToolbar',
|
||||
commandOptions: {
|
||||
bindings: [
|
||||
{
|
||||
mouseButton: 1, // Left Click
|
||||
},
|
||||
{
|
||||
mouseButton: 1, // Left Click+Shift to create a hole
|
||||
modifierKey: 16, // Shift
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Simplified Spline',
|
||||
type: 'switch',
|
||||
id: 'simplifiedSpline',
|
||||
value: true,
|
||||
commands: {
|
||||
commandName: 'setSimplifiedSplineForSplineContourSegmentationTool',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '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: 'Sculptor Tool',
|
||||
tooltip: 'Sculptor Tool',
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['SculptorTool'],
|
||||
disabledText: 'Create new segmentation to enable this tool.',
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Contour',
|
||||
},
|
||||
],
|
||||
commands: [
|
||||
'setToolActiveToolbar',
|
||||
{
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Contour',
|
||||
},
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: '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.'),
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['CircularBrush', 'SphereBrush'],
|
||||
disabledText: i18n.t('Buttons:Create new segmentation to enable this tool.'),
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
],
|
||||
commands: {
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
@@ -420,10 +671,12 @@ const toolbarButtons: Button[] = [
|
||||
max: 99.5,
|
||||
step: 0.5,
|
||||
value: 25,
|
||||
commands: {
|
||||
commandName: 'setBrushSize',
|
||||
commandOptions: { toolNames: ['CircularBrush', 'SphereBrush'] },
|
||||
},
|
||||
commands: [
|
||||
{
|
||||
commandName: 'setBrushSize',
|
||||
commandOptions: { toolNames: ['CircularBrush', 'SphereBrush'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Shape',
|
||||
@@ -434,44 +687,72 @@ const toolbarButtons: Button[] = [
|
||||
{ value: 'CircularBrush', label: 'Circle' },
|
||||
{ value: 'SphereBrush', label: 'Sphere' },
|
||||
],
|
||||
commands: 'setToolActiveToolbar',
|
||||
commands: ['setToolActiveToolbar'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'InterpolateLabelmap',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
uiType: 'ohif.toolButton',
|
||||
props: {
|
||||
icon: 'icon-tool-interpolation',
|
||||
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: [
|
||||
'evaluate.cornerstone.segmentation',
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
{
|
||||
name: 'evaluate.displaySetIsReconstructable',
|
||||
disabledText: i18n.t('Buttons:The current viewport cannot handle interpolation.'),
|
||||
},
|
||||
],
|
||||
commands: 'interpolateLabelmap',
|
||||
commands: [
|
||||
{
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
},
|
||||
'interpolateLabelmap',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'SegmentBidirectional',
|
||||
uiType: 'ohif.toolBoxButton',
|
||||
uiType: 'ohif.toolButton',
|
||||
props: {
|
||||
icon: 'icon-tool-bidirectional-segment',
|
||||
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.'),
|
||||
},
|
||||
commands: 'runSegmentBidirectional',
|
||||
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',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -483,12 +764,26 @@ const toolbarButtons: Button[] = [
|
||||
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.'),
|
||||
},
|
||||
commands: 'setToolActiveToolbar',
|
||||
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',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -503,7 +798,8 @@ const toolbarButtons: Button[] = [
|
||||
evaluate: [
|
||||
'evaluate.cornerstoneTool.toggle',
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentation',
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
],
|
||||
listeners: {
|
||||
@@ -512,7 +808,15 @@ const toolbarButtons: Button[] = [
|
||||
),
|
||||
[ViewportGridService.EVENTS.VIEWPORTS_READY]: callbacks('LabelmapSlicePropagation'),
|
||||
},
|
||||
commands: 'toggleEnabledDisabledToolbar',
|
||||
commands: [
|
||||
{
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
},
|
||||
'toggleEnabledDisabledToolbar',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -529,8 +833,20 @@ const toolbarButtons: Button[] = [
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['MarkerLabelmap', 'MarkerInclude', 'MarkerExclude'],
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
],
|
||||
commands: [
|
||||
'setToolActiveToolbar',
|
||||
{
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
},
|
||||
],
|
||||
commands: 'setToolActiveToolbar',
|
||||
listeners: {
|
||||
[ViewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: callbacks('MarkerLabelmap'),
|
||||
[ViewportGridService.EVENTS.VIEWPORTS_READY]: callbacks('MarkerLabelmap'),
|
||||
@@ -573,10 +889,16 @@ const toolbarButtons: Button[] = [
|
||||
props: {
|
||||
icon: 'icon-tool-eraser',
|
||||
label: i18n.t('Buttons:Eraser'),
|
||||
evaluate: {
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['CircularEraser', 'SphereEraser'],
|
||||
},
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['CircularEraser', 'SphereEraser'],
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
name: 'Radius (mm)',
|
||||
@@ -603,6 +925,12 @@ const toolbarButtons: Button[] = [
|
||||
commands: 'setToolActiveToolbar',
|
||||
},
|
||||
],
|
||||
commands: {
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -611,15 +939,28 @@ const toolbarButtons: Button[] = [
|
||||
props: {
|
||||
icon: 'icon-tool-threshold',
|
||||
label: 'Threshold Tool',
|
||||
evaluate: {
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: [
|
||||
'ThresholdCircularBrush',
|
||||
'ThresholdSphereBrush',
|
||||
'ThresholdCircularBrushDynamic',
|
||||
'ThresholdSphereBrushDynamic',
|
||||
],
|
||||
evaluate: [
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: [
|
||||
'ThresholdCircularBrush',
|
||||
'ThresholdSphereBrush',
|
||||
'ThresholdCircularBrushDynamic',
|
||||
'ThresholdSphereBrushDynamic',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
],
|
||||
commands: {
|
||||
commandName: 'activateSelectedSegmentationOfType',
|
||||
commandOptions: {
|
||||
segmentationRepresentationType: 'Labelmap',
|
||||
},
|
||||
},
|
||||
|
||||
options: [
|
||||
{
|
||||
name: 'Radius (mm)',
|
||||
@@ -728,10 +1069,22 @@ const toolbarButtons: Button[] = [
|
||||
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.'),
|
||||
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: [
|
||||
{
|
||||
@@ -749,6 +1102,81 @@ const toolbarButtons: Button[] = [
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
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: 'Labelmap Edit with Contour Tool',
|
||||
tooltip: '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',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default toolbarButtons;
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Sets up auto tab switching for when the first segmentation is added into the viewer.
|
||||
*/
|
||||
export default function setUpAutoTabSwitchHandler({
|
||||
segmentationService,
|
||||
viewportGridService,
|
||||
panelService,
|
||||
}) {
|
||||
const autoTabSwitchEvents = [
|
||||
segmentationService.EVENTS.SEGMENTATION_MODIFIED,
|
||||
segmentationService.EVENTS.SEGMENTATION_REPRESENTATION_MODIFIED,
|
||||
];
|
||||
|
||||
// Initially there are no segmentations, so we should switch the tab whenever the first segmentation is added.
|
||||
let shouldSwitchTab = true;
|
||||
|
||||
const unsubscribeAutoTabSwitchEvents = autoTabSwitchEvents
|
||||
.map(eventName =>
|
||||
segmentationService.subscribe(eventName, () => {
|
||||
const segmentations = segmentationService.getSegmentations();
|
||||
|
||||
if (!segmentations.length) {
|
||||
// If all the segmentations are removed, then the next time a segmentation is added, we should switch the tab.
|
||||
shouldSwitchTab = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const activeViewportId = viewportGridService.getActiveViewportId();
|
||||
const activeRepresentation = segmentationService
|
||||
.getSegmentationRepresentations(activeViewportId)
|
||||
?.find(representation => representation.active);
|
||||
|
||||
if (activeRepresentation && shouldSwitchTab) {
|
||||
shouldSwitchTab = false;
|
||||
|
||||
switch (activeRepresentation.type) {
|
||||
case 'Labelmap':
|
||||
panelService.activatePanel(
|
||||
'@ohif/extension-cornerstone.panelModule.panelSegmentationWithToolsLabelMap',
|
||||
true
|
||||
);
|
||||
break;
|
||||
case 'Contour':
|
||||
panelService.activatePanel(
|
||||
'@ohif/extension-cornerstone.panelModule.panelSegmentationWithToolsContour',
|
||||
true
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
.map(subscription => subscription.unsubscribe);
|
||||
|
||||
return { unsubscribeAutoTabSwitchEvents };
|
||||
}
|
||||
Reference in new issue
Block a user