From 78df8ff03a371904a390c63a95628d5511dad136 Mon Sep 17 00:00:00 2001 From: Devu-trenser <91659097+Devu-trenser@users.noreply.github.com> Date: Wed, 8 Oct 2025 03:10:32 +0530 Subject: [PATCH] fix(segmentation): Segmentation not displayed and unable to draw segments when switching back to volume/stack viewport from 3D viewport (#5430) Co-authored-by: Vinicius Faria --- .../SegmentationService.test.ts | 43 ++++++++++++++++--- .../SegmentationService.ts | 6 ++- .../CornerstoneViewportService.ts | 15 ++++++- 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/extensions/cornerstone/src/services/SegmentationService/SegmentationService.test.ts b/extensions/cornerstone/src/services/SegmentationService/SegmentationService.test.ts index eed05265f..37643eb08 100644 --- a/extensions/cornerstone/src/services/SegmentationService/SegmentationService.test.ts +++ b/extensions/cornerstone/src/services/SegmentationService/SegmentationService.test.ts @@ -648,7 +648,6 @@ describe('SegmentationService', () => { jest .spyOn(cstSegmentation.state, 'updateLabelmapSegmentationImageReferences') .mockReturnValue(undefined); - // @ts-expect-error - getLabelmapImageIds is wrongly typed at cornerstone3D jest.spyOn(cstSegmentation, 'getLabelmapImageIds').mockReturnValue([imageId]); jest .spyOn(cstSegmentation, 'addSegmentationRepresentations') @@ -949,6 +948,37 @@ describe('SegmentationService', () => { }); }); + it('should add a surface segmentation representation to volume viewport without need for handling', async () => { + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation as cstTypes.Segmentation); + jest + .spyOn(serviceManagerMock.services.cornerstoneViewportService, 'getCornerstoneViewport') + // only needed interfaces for the addSegmentationRepresentation call + .mockReturnValue({ + ...mockCornerstoneVolumeViewport, + type: ViewportType.VOLUME_3D, + } as unknown as csTypes.IVolumeViewport); + jest + .spyOn(cstSegmentation, 'addSegmentationRepresentations') + .mockReturnValueOnce(undefined); + + await service.addSegmentationRepresentation(viewportId, { + segmentationId: mockCornerstoneSegmentation.segmentationId, + }); + + expect(serviceManagerMock.services.viewportGridService.getState).not.toHaveBeenCalled(); + + expect(cstSegmentation.addSegmentationRepresentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.addSegmentationRepresentations).toHaveBeenCalledWith(viewportId, [ + { + type: csToolsEnums.SegmentationRepresentations.Surface, + segmentationId: mockCornerstoneSegmentation.segmentationId, + config: { colorLUTOrIndex: undefined }, + }, + ]); + }); + it('should add a volume segmentation representation to volume viewport through SegmentationService handling', async () => { mockCornerstoneVolumeViewport.type = ViewportType.ORTHOGRAPHIC; jest @@ -1007,7 +1037,6 @@ describe('SegmentationService', () => { jest .spyOn(cstSegmentation, 'addSegmentationRepresentations') .mockReturnValueOnce(undefined); - // @ts-expect-error - getLabelmapImageIds is wrongly typed at cornerstone3D jest.spyOn(cstSegmentation, 'getLabelmapImageIds').mockReturnValue([imageId]); jest .spyOn(cache, 'getImage') @@ -2033,7 +2062,7 @@ describe('SegmentationService', () => { jest.spyOn(cstSegmentation.config.color, 'setSegmentIndexColor').mockReturnValue(undefined); jest .spyOn(cstSegmentation.state, 'getSegmentationRepresentations') - .mockReturnValue([{ colorLUTIndex: 1 }]); + .mockReturnValue([{ colorLUTIndex: 1 }] as cstTypes.SegmentationRepresentation[]); service.addSegment(segmentationId, config); @@ -2203,7 +2232,7 @@ describe('SegmentationService', () => { it('should set the color of the segment', () => { jest .spyOn(cstSegmentation.state, 'getSegmentationRepresentations') - .mockReturnValue([{ colorLUTIndex: 1 }]); + .mockReturnValue([{ colorLUTIndex: 1 }] as cstTypes.SegmentationRepresentation[]); jest.spyOn(cstSegmentation.config.color, 'setSegmentIndexColor').mockReturnValue(undefined); service.setSegmentColor(viewportId, segmentationId, segmentIndex, color); @@ -2226,7 +2255,7 @@ describe('SegmentationService', () => { it('should set the color of the segment with the colorLUTIndex', async () => { jest .spyOn(cstSegmentation.state, 'getSegmentationRepresentations') - .mockReturnValue([{ colorLUTIndex: 1 }]); + .mockReturnValue([{ colorLUTIndex: 1 }] as cstTypes.SegmentationRepresentation[]); jest.spyOn(cstSegmentation.config.color, 'setSegmentIndexColor').mockReturnValue(undefined); service.setSegmentColor(viewportId, segmentationId, segmentIndex, color); @@ -2246,7 +2275,7 @@ describe('SegmentationService', () => { await service.addSegmentationRepresentation(viewportId, { segmentationId: segmentationId, type: csToolsEnums.SegmentationRepresentations.Labelmap, - config: { active: true }, + config: { blendMode: csEnums.BlendModes.COMPOSITE }, suppressEvents: true, }); @@ -2255,7 +2284,7 @@ describe('SegmentationService', () => { { type: csToolsEnums.SegmentationRepresentations.Labelmap, segmentationId: segmentationId, - config: { colorLUTOrIndex: 1, active: true }, + config: { colorLUTOrIndex: 1, blendMode: csEnums.BlendModes.COMPOSITE }, }, ]); }); diff --git a/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts b/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts index 6946fd897..cfd09a09d 100644 --- a/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts +++ b/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts @@ -295,10 +295,12 @@ class SegmentationService extends PubSubService { const colorLUTIndex = this._segmentationIdToColorLUTIndexMap.get(segmentationId); - const defaultRepresentationType = LABELMAP; - let representationTypeToUse = type || defaultRepresentationType; let isConverted = false; + const defaultRepresentationType: csToolsEnums.SegmentationRepresentations = + csViewport.type === ViewportType.VOLUME_3D ? SURFACE : LABELMAP; + let representationTypeToUse = type || defaultRepresentationType; + if (representationTypeToUse === LABELMAP) { const { isVolumeViewport, isVolumeSegmentation } = this.determineViewportAndSegmentationType( csViewport, diff --git a/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts b/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts index 981d21022..765ab204d 100644 --- a/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts +++ b/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts @@ -1335,10 +1335,23 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi segmentationPresentation.forEach((presentationItem: SegmentationPresentationItem) => { const { segmentationId, type, hydrated } = presentationItem; + const { Labelmap, Surface } = csToolsEnums.SegmentationRepresentations; + const representationType = (() => { + if (type === Surface) { + if (viewport.type === csEnums.ViewportType.VOLUME_3D) { + return Surface; + } else { + return Labelmap; + } + } + + return type; + })(); + if (hydrated) { segmentationService.addSegmentationRepresentation(viewport.id, { segmentationId, - type, + type: representationType, }); } });