285 lines
8.8 KiB
TypeScript
285 lines
8.8 KiB
TypeScript
import { useEffect, useMemo, useState } from 'react';
|
|
import { useSystem, utils } from '@ohif/core';
|
|
import { useViewportGrid } from '@ohif/ui-next';
|
|
import {
|
|
getEnhancedDisplaySets,
|
|
sortByOverlayable,
|
|
DERIVED_OVERLAY_MODALITIES,
|
|
} from '../components/ViewportDataOverlaySettingMenu/utils';
|
|
|
|
const sortByPriority = (a, b) => {
|
|
if (utils.isLowPriorityModality(a.Modality)) {
|
|
return 1;
|
|
}
|
|
return -1;
|
|
};
|
|
|
|
/**
|
|
* Options for the useViewportDisplaySets hook
|
|
*/
|
|
export type UseViewportDisplaySetsOptions = {
|
|
/**
|
|
* Whether to include background display set
|
|
*/
|
|
includeBackground?: boolean;
|
|
/**
|
|
* Whether to include foreground display sets
|
|
*/
|
|
includeForeground?: boolean;
|
|
/**
|
|
* Whether to include overlay display sets
|
|
*/
|
|
includeOverlay?: boolean;
|
|
/**
|
|
* Whether to include potential overlay display sets
|
|
*/
|
|
includePotentialOverlay?: boolean;
|
|
/**
|
|
* Whether to include potential foreground display sets
|
|
*/
|
|
includePotentialForeground?: boolean;
|
|
/**
|
|
* Whether to include potential background display sets
|
|
*/
|
|
includePotentialBackground?: boolean;
|
|
};
|
|
|
|
/**
|
|
* Return type for useViewportDisplaySets
|
|
*/
|
|
export type ViewportDisplaySets = {
|
|
/**
|
|
* All display sets for the viewport
|
|
*/
|
|
allDisplaySets: AppTypes.DisplaySet[];
|
|
/**
|
|
* The viewport display sets for the viewport
|
|
*/
|
|
viewportDisplaySets: AppTypes.DisplaySet[];
|
|
/**
|
|
* The primary display set for the viewport (base image)
|
|
*/
|
|
backgroundDisplaySet?: AppTypes.DisplaySet;
|
|
/**
|
|
* Display sets currently shown with background (non-overlay layers)
|
|
*/
|
|
foregroundDisplaySets?: AppTypes.DisplaySet[];
|
|
/**
|
|
* Segmentation display sets currently applied as overlays
|
|
*/
|
|
overlayDisplaySets?: AppTypes.DisplaySet[];
|
|
/**
|
|
* Display sets that could be toggled on as overlays (derived modalities)
|
|
*/
|
|
potentialOverlayDisplaySets?: AppTypes.DisplaySet[];
|
|
/**
|
|
* Display sets that could be added as foreground layers
|
|
*/
|
|
potentialForegroundDisplaySets?: AppTypes.DisplaySet[];
|
|
/**
|
|
* Display sets that could replace the current background
|
|
*/
|
|
potentialBackgroundDisplaySets?: AppTypes.DisplaySet[];
|
|
};
|
|
|
|
/**
|
|
* Hook to provide display sets and overlay information for a viewport based on options.
|
|
*
|
|
* @param viewportId - The viewport ID to get display sets for
|
|
* @param options - Options to control which display sets to compute
|
|
* @returns Object containing requested display set collections based on options
|
|
*/
|
|
export function useViewportDisplaySets(
|
|
viewportId?: string,
|
|
options?: UseViewportDisplaySetsOptions
|
|
): ViewportDisplaySets {
|
|
const { servicesManager } = useSystem();
|
|
const { displaySetService, segmentationService } = servicesManager.services;
|
|
|
|
// Note: this is very important we should use the useViewportGrid hook here,
|
|
// since if the viewport displaySet is changed we should re-run this hook
|
|
// to get the latest displaySets
|
|
const [viewportGridState, viewportGridService] = useViewportGrid();
|
|
|
|
const viewportIdToUse = viewportId || viewportGridState.activeViewportId;
|
|
|
|
// Apply defaults - include everything if no options specified
|
|
const {
|
|
includeBackground = true,
|
|
includeForeground = true,
|
|
includeOverlay = true,
|
|
includePotentialOverlay = true,
|
|
includePotentialForeground = true,
|
|
includePotentialBackground = true,
|
|
} = options || {};
|
|
|
|
// Get all available display sets (only if needed)
|
|
const needsAllDisplaySets = includePotentialBackground;
|
|
const allDisplaySets = useMemo(
|
|
() => (needsAllDisplaySets ? displaySetService.getActiveDisplaySets() : []),
|
|
[displaySetService, needsAllDisplaySets]
|
|
);
|
|
|
|
// Get all available segmentations (only if needed)
|
|
const needsSegmentations = includeOverlay;
|
|
|
|
const [segmentationRepresentations, setSegmentationRepresentations] = useState(
|
|
needsSegmentations ? segmentationService.getSegmentationRepresentations(viewportIdToUse) : []
|
|
);
|
|
|
|
useEffect(() => {
|
|
setSegmentationRepresentations(
|
|
needsSegmentations ? segmentationService.getSegmentationRepresentations(viewportIdToUse) : []
|
|
);
|
|
|
|
const unsubscribeArr = needsSegmentations
|
|
? [
|
|
segmentationService.EVENTS.SEGMENTATION_REPRESENTATION_MODIFIED,
|
|
segmentationService.EVENTS.SEGMENTATION_REPRESENTATION_REMOVED,
|
|
].map(event =>
|
|
segmentationService.subscribe(event, () => {
|
|
setSegmentationRepresentations(
|
|
segmentationService.getSegmentationRepresentations(viewportIdToUse)
|
|
);
|
|
})
|
|
)
|
|
: [];
|
|
|
|
return () => {
|
|
unsubscribeArr.forEach(item => item.unsubscribe());
|
|
};
|
|
}, [segmentationService, viewportIdToUse, needsSegmentations]);
|
|
|
|
const overlayDisplaySets = useMemo(() => {
|
|
if (!includeOverlay) {
|
|
return [];
|
|
}
|
|
return segmentationRepresentations.map(repr => {
|
|
const displaySet = displaySetService.getDisplaySetByUID(repr.segmentationId);
|
|
return displaySet;
|
|
});
|
|
}, [includeOverlay, segmentationRepresentations, displaySetService]);
|
|
|
|
const overlayDisplaySetUIDs = useMemo(() => {
|
|
return overlayDisplaySets.map(ds => ds.displaySetInstanceUID);
|
|
}, [overlayDisplaySets]);
|
|
|
|
// Get enhanced display sets (only if needed)
|
|
const needsEnhancedDisplaySets =
|
|
includeBackground || includeForeground || includePotentialOverlay || includePotentialForeground;
|
|
|
|
const { viewportDisplaySets = [], enhancedDisplaySets = [] } = useMemo(() => {
|
|
if (!needsEnhancedDisplaySets) {
|
|
return { viewportDisplaySets: [], enhancedDisplaySets: [] };
|
|
}
|
|
return (
|
|
getEnhancedDisplaySets({
|
|
viewportId: viewportIdToUse,
|
|
services: { displaySetService, viewportGridService },
|
|
}) || { viewportDisplaySets: [], enhancedDisplaySets: [] }
|
|
);
|
|
}, [viewportIdToUse, displaySetService, viewportGridService, needsEnhancedDisplaySets]);
|
|
|
|
const backgroundDisplaySet = useMemo(
|
|
() =>
|
|
includeBackground && viewportDisplaySets.length > 0 ? viewportDisplaySets[0] : undefined,
|
|
[includeBackground, viewportDisplaySets]
|
|
);
|
|
|
|
const foregroundDisplaySets = useMemo(() => {
|
|
if (!includeForeground || !backgroundDisplaySet) {
|
|
return [];
|
|
}
|
|
return viewportDisplaySets.filter(
|
|
ds =>
|
|
!DERIVED_OVERLAY_MODALITIES.includes(ds.Modality) &&
|
|
ds.displaySetInstanceUID !== backgroundDisplaySet.displaySetInstanceUID
|
|
);
|
|
}, [includeForeground, viewportDisplaySets, backgroundDisplaySet]);
|
|
|
|
const foregroundDisplaySetUIDs = useMemo(
|
|
() => foregroundDisplaySets.map(ds => ds.displaySetInstanceUID),
|
|
[foregroundDisplaySets]
|
|
);
|
|
|
|
const potentialOverlayDisplaySets = useMemo(() => {
|
|
if (!includePotentialOverlay) {
|
|
return [];
|
|
}
|
|
return enhancedDisplaySets
|
|
.filter(
|
|
ds =>
|
|
DERIVED_OVERLAY_MODALITIES.includes(ds.Modality) &&
|
|
!overlayDisplaySetUIDs.includes(ds.displaySetInstanceUID) &&
|
|
ds.isOverlayable
|
|
)
|
|
.sort(sortByOverlayable);
|
|
}, [includePotentialOverlay, enhancedDisplaySets, overlayDisplaySetUIDs]);
|
|
|
|
const potentialForegroundDisplaySets = useMemo(() => {
|
|
if (!includePotentialForeground) {
|
|
return [];
|
|
}
|
|
return enhancedDisplaySets
|
|
.filter(
|
|
ds =>
|
|
!DERIVED_OVERLAY_MODALITIES.includes(ds.Modality) &&
|
|
!foregroundDisplaySetUIDs.includes(ds.displaySetInstanceUID) &&
|
|
ds.isOverlayable
|
|
)
|
|
.sort(sortByPriority);
|
|
}, [includePotentialForeground, enhancedDisplaySets, foregroundDisplaySetUIDs]);
|
|
|
|
const potentialBackgroundDisplaySets = useMemo(() => {
|
|
if (!includePotentialBackground || !backgroundDisplaySet) {
|
|
return [];
|
|
}
|
|
return allDisplaySets
|
|
.filter(
|
|
ds =>
|
|
!DERIVED_OVERLAY_MODALITIES.includes(ds.Modality) &&
|
|
ds.displaySetInstanceUID !== backgroundDisplaySet.displaySetInstanceUID &&
|
|
!overlayDisplaySetUIDs.includes(ds.displaySetInstanceUID) &&
|
|
!foregroundDisplaySetUIDs.includes(ds.displaySetInstanceUID)
|
|
)
|
|
.sort(sortByPriority);
|
|
}, [
|
|
includePotentialBackground,
|
|
allDisplaySets,
|
|
backgroundDisplaySet,
|
|
overlayDisplaySetUIDs,
|
|
foregroundDisplaySetUIDs,
|
|
]);
|
|
|
|
const result: ViewportDisplaySets = {
|
|
allDisplaySets: allDisplaySets || [],
|
|
viewportDisplaySets: viewportDisplaySets || [],
|
|
};
|
|
|
|
if (includeBackground) {
|
|
result.backgroundDisplaySet = backgroundDisplaySet;
|
|
}
|
|
|
|
if (includeForeground) {
|
|
result.foregroundDisplaySets = foregroundDisplaySets;
|
|
}
|
|
|
|
if (includeOverlay) {
|
|
result.overlayDisplaySets = overlayDisplaySets;
|
|
}
|
|
|
|
if (includePotentialOverlay) {
|
|
result.potentialOverlayDisplaySets = potentialOverlayDisplaySets;
|
|
}
|
|
|
|
if (includePotentialForeground) {
|
|
result.potentialForegroundDisplaySets = potentialForegroundDisplaySets;
|
|
}
|
|
|
|
if (includePotentialBackground) {
|
|
result.potentialBackgroundDisplaySets = potentialBackgroundDisplaySets;
|
|
}
|
|
|
|
return result;
|
|
}
|