diff --git a/extensions/cornerstone-dicom-seg/src/getToolbarModule.ts b/extensions/cornerstone-dicom-seg/src/getToolbarModule.ts index ab1dcd58b35..f2d7aec8291 100644 --- a/extensions/cornerstone-dicom-seg/src/getToolbarModule.ts +++ b/extensions/cornerstone-dicom-seg/src/getToolbarModule.ts @@ -17,6 +17,15 @@ export function getToolbarModule({ servicesManager }: withAppTypes) { }; } + const activeSegmentation = segmentationService.getActiveSegmentation(viewportId); + if (!Object.keys(activeSegmentation.segments).length) { + return { + disabled: true, + className: '!text-common-bright !bg-black opacity-50', + disabledText: 'Add segment to enable this tool', + }; + } + const toolGroup = toolGroupService.getToolGroupForViewport(viewportId); if (!toolGroup) { diff --git a/extensions/cornerstone/src/index.tsx b/extensions/cornerstone/src/index.tsx index 41f4c33ff0d..d51c8ff51c6 100644 --- a/extensions/cornerstone/src/index.tsx +++ b/extensions/cornerstone/src/index.tsx @@ -96,6 +96,7 @@ const cornerstoneExtension: Types.Extensions.Extension = { toolbarService.registerEventForToolbarUpdate(segmentationService, [ segmentationService.EVENTS.SEGMENTATION_REMOVED, segmentationService.EVENTS.SEGMENTATION_MODIFIED, + segmentationService.EVENTS.SEGMENTATION_CHANGED, ]); toolbarService.registerEventForToolbarUpdate(cornerstone.eventTarget, [ diff --git a/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts b/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts index b2595585459..e30ad66cb3e 100644 --- a/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts +++ b/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts @@ -28,6 +28,7 @@ import { updateLabelmapSegmentationImageReferences } from '@cornerstonejs/tools/ import { triggerSegmentationRepresentationModified } from '@cornerstonejs/tools/segmentation/triggerSegmentationEvents'; import { convertStackToVolumeLabelmap } from '@cornerstonejs/tools/segmentation/helpers/convertStackToVolumeLabelmap'; import { getLabelmapImageIds } from '@cornerstonejs/tools/segmentation'; +import setSegToolModeForToolGroups from '../../utils/setSegToolModeForToolGroups'; const LABELMAP = csToolsEnums.SegmentationRepresentations.Labelmap; const CONTOUR = csToolsEnums.SegmentationRepresentations.Contour; @@ -64,6 +65,8 @@ const EVENTS = { SEGMENTATION_DATA_MODIFIED: 'event::segmentation_data_modified', // fired when the segmentation is removed SEGMENTATION_REMOVED: 'event::segmentation_removed', + // fired when the segmentation is changed + SEGMENTATION_CHANGED: 'event::segmentation_changed', // // fired when segmentation representation is added SEGMENTATION_REPRESENTATION_MODIFIED: 'event::segmentation_representation_modified', @@ -361,11 +364,11 @@ class SegmentationService extends PubSubService { options.segments && Object.keys(options.segments).length > 0 ? options.segments : { - 1: { - label: 'Segment 1', - active: true, + 1: { + label: 'Segment 1', + active: true, + }, }, - }, cachedStats: { info: `S${displaySet.SeriesNumber}: ${displaySet.SeriesDescription}`, }, @@ -382,8 +385,8 @@ class SegmentationService extends PubSubService { segmentationId?: string; type: SegmentationRepresentations; } = { - type: LABELMAP, - } + type: LABELMAP, + } ): Promise { const { type } = options; let { segmentationId } = options; @@ -535,8 +538,8 @@ class SegmentationService extends PubSubService { segmentationId?: string; type: SegmentationRepresentations; } = { - type: CONTOUR, - } + type: CONTOUR, + } ): Promise { const { type } = options; let { segmentationId } = options; @@ -687,10 +690,28 @@ class SegmentationService extends PubSubService { // Add a new segmentation this.addSegmentationToSource(data as cstTypes.SegmentationPublicInput); } + const { toolGroupService } = this.servicesManager.services; + setSegToolModeForToolGroups(toolGroupService, csToolsEnums.ToolModes.Active); } public setActiveSegmentation(viewportId: string, segmentationId: string): void { + const segmentation = this.getSegmentation(segmentationId); + const segments = segmentation.segments; + const { toolGroupService } = this.servicesManager.services; + + // Set the tool mode based on the presence of segments + const toolMode = Object.keys(segments).length + ? csToolsEnums.ToolModes.Active + : csToolsEnums.ToolModes.Passive; + cstSegmentation.activeSegmentation.setActiveSegmentation(viewportId, segmentationId); + + // Set the tool mode for the tool group + setSegToolModeForToolGroups(toolGroupService, toolMode); + + this._broadcastEvent(EVENTS.SEGMENTATION_CHANGED, { + segmentationId, + }); } /** @@ -808,7 +829,7 @@ class SegmentationService extends PubSubService { if (!segmentIndex) { // grab the next available segment index based on the object keys, // so basically get the highest segment index value + 1 - segmentIndex = Math.max(...Object.keys(csSegmentation.segments).map(Number)) + 1; + segmentIndex = Math.max(0, ...Object.keys(csSegmentation.segments).map(Number)) + 1; } // update the segmentation @@ -874,6 +895,15 @@ class SegmentationService extends PubSubService { */ public removeSegment(segmentationId: string, segmentIndex: number): void { cstSegmentation.removeSegment(segmentationId, segmentIndex); + const segmentation = this.getSegmentation(segmentationId); + const segments = segmentation.segments; + + // Set the tool to passive mode if there are no segments in the active segmentation. + if (!Object.keys(segments).length) { + const { toolGroupService } = this.servicesManager.services; + + setSegToolModeForToolGroups(toolGroupService, csToolsEnums.ToolModes.Passive); + } } public setSegmentVisibility( @@ -1704,7 +1734,17 @@ class SegmentationService extends PubSubService { }; private _setActiveSegment(segmentationId: string, segmentIndex: number) { + const segmentation = this.getSegmentation(segmentationId); + const segments = segmentation.segments; + cstSegmentation.segmentIndex.setActiveSegmentIndex(segmentationId, segmentIndex); + + // Set the tool to active mode if there is at least one segment in the active segmentation. + if (Object.keys(segments).length) { + const { toolGroupService } = this.servicesManager.services; + + setSegToolModeForToolGroups(toolGroupService, csToolsEnums.ToolModes.Active); + } } private _getVolumeIdForDisplaySet(displaySet) { diff --git a/extensions/cornerstone/src/utils/SegmentationTools.ts b/extensions/cornerstone/src/utils/SegmentationTools.ts new file mode 100644 index 00000000000..d9ceb195d53 --- /dev/null +++ b/extensions/cornerstone/src/utils/SegmentationTools.ts @@ -0,0 +1,14 @@ +const segmentationTools = [ + 'CircularBrush', + 'SphereBrush', + 'CircularEraser', + 'SphereEraser', + 'CircleScissor', + 'RectangleScissor', + 'SphereScissor', + 'ThresholdCircularBrush', + 'ThresholdSphereBrush', + 'ThresholdCircularBrushDynamic', +]; + +export { segmentationTools }; diff --git a/extensions/cornerstone/src/utils/setSegToolModeForToolGroups.ts b/extensions/cornerstone/src/utils/setSegToolModeForToolGroups.ts new file mode 100644 index 00000000000..b8c616ab476 --- /dev/null +++ b/extensions/cornerstone/src/utils/setSegToolModeForToolGroups.ts @@ -0,0 +1,33 @@ +import { Enums } from '@cornerstonejs/tools'; +import ToolGroupService from '../services/ToolGroupService'; +import { segmentationTools } from './SegmentationTools'; + +/** + * Sets the mode for segmentation tools in all tool groups managed by the tool group service. + * + * @param toolGroupService - The service managing the tool groups. + * @param mode - The mode to set for the tools (e.g., Active, Enabled, Passive, Disabled). + */ +const setSegToolModeForToolGroups = (toolGroupService: ToolGroupService, mode: Enums.ToolModes) => { + const toolGroupIds = toolGroupService.getToolGroupIds(); + toolGroupIds.forEach(toolGroupId => { + const toolGroup = toolGroupService.getToolGroup(toolGroupId); + if (!toolGroup) { + return; + } + const toolName = toolGroup.currentActivePrimaryToolName; + if (!toolName || !toolGroup.hasTool(toolName) || !segmentationTools.includes(toolName)) { + return; + } + let options; + + if (mode === Enums.ToolModes.Active) { + options = { + bindings: [{ mouseButton: Enums.MouseBindings.Primary }], + }; + } + toolGroup.setToolMode(toolName, mode, options); + }); +}; + +export default setSegToolModeForToolGroups;