ohif-viewer/extensions/cornerstone/src/services/SyncGroupService/createHydrateSegmentationSynchronizer.ts
Ghadeer Albattarni b9029ef6f8
fix(segmentation): restrict overlay segmentation menu to same frame of reference as viewport background display set (#5900)
- Add FrameOfReferenceUID to SEG and RTSTRUCT displaySet in SOP Class Handlers so the FOR is available for filtering
- Sync optimisticOverlayDisplaySets when background display set changes so the overlay menu reflects the correct state after a background switch
- Add FOR matching guard to the hydrate segmentation synchronizer to prevent the hydration synchronizer from blindly mirroring segmentations from a source viewport to a target viewport if their primary Frames of Reference do not align.
- fix segmentation overlay order reversal on viewport re-render
2026-04-02 08:52:23 -04:00

113 lines
3.9 KiB
TypeScript

import { Enums as CoreEnums, Types, getEnabledElementByViewportId } from '@cornerstonejs/core';
import {
SynchronizerManager,
Synchronizer,
Enums,
Types as ToolsTypes,
} from '@cornerstonejs/tools';
import { isAnyDisplaySetCommon } from '../../utils/isAnyDisplaySetCommon';
const { createSynchronizer } = SynchronizerManager;
const { SEGMENTATION_REPRESENTATION_MODIFIED } = Enums.Events;
const { BlendModes } = CoreEnums;
export default function createHydrateSegmentationSynchronizer(
synchronizerName: string,
{ servicesManager, ...options }: { servicesManager: AppTypes.ServicesManager; options }
): Synchronizer {
const stackImageSynchronizer = createSynchronizer(
synchronizerName,
SEGMENTATION_REPRESENTATION_MODIFIED,
(synchronizerInstance, sourceViewport, targetViewport, sourceEvent) => {
return segmentationRepresentationModifiedCallback(
synchronizerInstance,
sourceViewport,
targetViewport,
sourceEvent,
{ servicesManager, options }
);
},
{
eventSource: 'eventTarget',
}
);
return stackImageSynchronizer;
}
/**
* This method will add the segmentation representation to any target viewports having:
*
* 1. the same FrameOfReferenceUID (FOR) as the segmentation representation, or
* 2. a shared DisplaySet with the source viewport when no FOR is present.
*/
const segmentationRepresentationModifiedCallback = async (
synchronizerInstance: Synchronizer,
sourceViewport: Types.IViewportId,
targetViewport: Types.IViewportId,
sourceEvent: Event,
{ servicesManager, options }: { servicesManager: AppTypes.ServicesManager; options: unknown }
) => {
const event = sourceEvent as ToolsTypes.EventTypes.SegmentationRepresentationModifiedEventType;
const { segmentationId, type: segmentationRepresentationType } = event.detail;
const { segmentationService, cornerstoneViewportService } = servicesManager.services;
const targetViewportId = targetViewport.viewportId;
const sourceViewportId = sourceViewport.viewportId;
const { viewport } = getEnabledElementByViewportId(targetViewportId);
const sourceViewportInfo = cornerstoneViewportService.getViewportInfo(sourceViewportId);
const targetViewportInfo = cornerstoneViewportService.getViewportInfo(targetViewportId);
const sourceDisplaySetUIDs = extractDisplaySetUIDs(sourceViewportInfo);
const targetDisplaySetUIDs = extractDisplaySetUIDs(targetViewportInfo);
const sharedDisplaySetExists = isAnyDisplaySetCommon(sourceDisplaySetUIDs, targetDisplaySetUIDs);
const targetFrameOfReferenceUID = viewport.getFrameOfReferenceUID();
const sourceFrameOfReferenceUID =
getEnabledElementByViewportId(sourceViewportId)?.viewport?.getFrameOfReferenceUID();
if (!sharedDisplaySetExists && !targetFrameOfReferenceUID) {
return;
}
if (!sharedDisplaySetExists && targetFrameOfReferenceUID !== sourceFrameOfReferenceUID) {
return;
}
const targetViewportRepresentation = segmentationService.getSegmentationRepresentations(
targetViewportId,
{ segmentationId }
);
if (targetViewportRepresentation.length > 0) {
return;
}
// Ensure the segmentation representation aligns with the target viewport type.
const type: Enums.SegmentationRepresentations =
viewport.type === CoreEnums.ViewportType.VOLUME_3D
? Enums.SegmentationRepresentations.Surface
: ((segmentationRepresentationType as Enums.SegmentationRepresentations) ??
Enums.SegmentationRepresentations.Labelmap);
await segmentationService.addSegmentationRepresentation(targetViewportId, {
segmentationId,
type,
config: {
blendMode:
viewport?.getBlendMode?.() === 1 ? BlendModes.LABELMAP_EDGE_PROJECTION_BLEND : undefined,
},
});
};
/**
* Extracts the displaySetInstanceUIDs from a viewportInfo.
*/
function extractDisplaySetUIDs(viewportInfo) {
return viewportInfo.getViewportData().data.map(ds => ds.displaySetInstanceUID);
}