feat(data overlay): add foreground background control plus orientation change in the UI and add RTDOSE support (#5007)
This commit is contained in:
1 parent
c1fced0573
commit
cd4e6408bc
105 files changed
+3117
-788
No files matched your search
@@ -0,0 +1,5 @@
|
||||
export { default as useViewportActionCorners } from './useViewportActionCorners';
|
||||
export * from './useViewportActionCorners';
|
||||
export * from './useActiveViewportSegmentationRepresentations';
|
||||
export * from './useMeasurements';
|
||||
export * from './useSegmentations';
|
||||
@@ -84,7 +84,7 @@ function mapSegmentationToDisplay(segmentation, customizationService) {
|
||||
* @param options.debounceTime - Debounce time in milliseconds for updates.
|
||||
* @returns An array of segmentation data.
|
||||
*/
|
||||
export function useSegmentations(options: {
|
||||
export function useSegmentations(options?: {
|
||||
subscribeToDataModified?: boolean;
|
||||
debounceTime?: number;
|
||||
}): SegmentationData[] {
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
import { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { ViewportActionCornersLocations } from '@ohif/ui-next';
|
||||
import { useSystem } from '@ohif/core/src';
|
||||
|
||||
/**
|
||||
* Viewport action corner location type
|
||||
*/
|
||||
export interface ViewportActionCornerLocation {
|
||||
type: 'VIEWPORT';
|
||||
id: string;
|
||||
corner?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
||||
}
|
||||
|
||||
/**
|
||||
* Viewport action corner service interface
|
||||
*/
|
||||
export interface ViewportActionCornerService {
|
||||
addComponent: (params: {
|
||||
viewportId: string;
|
||||
id: string;
|
||||
component: React.ReactNode;
|
||||
location: ViewportActionCornersLocations | string;
|
||||
indexPriority?: number;
|
||||
}) => void;
|
||||
clear: (viewportId: string) => void;
|
||||
getAlignAndSide: (location: ViewportActionCornersLocations | string) => {
|
||||
align: 'start' | 'center' | 'end';
|
||||
side: 'top' | 'right' | 'bottom' | 'left';
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Customization service interface
|
||||
*/
|
||||
export interface CustomizationService {
|
||||
getCustomization: (name: string) => any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for the useViewportActionCorners hook
|
||||
*/
|
||||
interface UseViewportActionCornersProps {
|
||||
viewportId: string;
|
||||
elementRef: React.MutableRefObject<HTMLDivElement>;
|
||||
displaySets: AppTypes.DisplaySet[];
|
||||
}
|
||||
|
||||
// Map of customization keys to their corresponding enum values
|
||||
const locationMap = {
|
||||
'viewportActionMenu.topLeft': ViewportActionCornersLocations.topLeft,
|
||||
'viewportActionMenu.topRight': ViewportActionCornersLocations.topRight,
|
||||
'viewportActionMenu.bottomLeft': ViewportActionCornersLocations.bottomLeft,
|
||||
'viewportActionMenu.bottomRight': ViewportActionCornersLocations.bottomRight,
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to manage viewport action corners for a Cornerstone viewport
|
||||
*/
|
||||
export function useViewportActionCorners({
|
||||
viewportId,
|
||||
elementRef,
|
||||
displaySets,
|
||||
}: UseViewportActionCornersProps): void {
|
||||
const { servicesManager } = useSystem();
|
||||
const { viewportActionCornersService, customizationService } = servicesManager.services;
|
||||
|
||||
useEffect(() => {
|
||||
// Process each location
|
||||
Object.entries(locationMap).forEach(([locationKey, locationValue]) => {
|
||||
const items = customizationService.getCustomization(locationKey);
|
||||
|
||||
if (!items || !items.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
items.forEach(item => {
|
||||
if (!item.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const componentId = item.id;
|
||||
|
||||
if (item.component) {
|
||||
// Use the component renderer provided directly in the item
|
||||
const component = item.component({
|
||||
viewportId,
|
||||
element: elementRef.current,
|
||||
displaySets,
|
||||
location: locationValue,
|
||||
});
|
||||
|
||||
viewportActionCornersService.addComponent({
|
||||
viewportId,
|
||||
id: componentId,
|
||||
component,
|
||||
location: locationValue,
|
||||
});
|
||||
} else if (item.component) {
|
||||
// Handle static components
|
||||
viewportActionCornersService.addComponent({
|
||||
viewportId,
|
||||
id: item.id,
|
||||
component: item.component,
|
||||
location: locationValue,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}, [displaySets, viewportId, viewportActionCornersService, elementRef]);
|
||||
}
|
||||
|
||||
export default useViewportActionCorners;
|
||||
@@ -0,0 +1,247 @@
|
||||
import { useMemo } 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 = {
|
||||
/**
|
||||
* The primary display set for the viewport (base image)
|
||||
*/
|
||||
backgroundDisplaySet?: any;
|
||||
/**
|
||||
* Display sets currently shown with background (non-overlay layers)
|
||||
*/
|
||||
foregroundDisplaySets?: any[];
|
||||
/**
|
||||
* Segmentation display sets currently applied as overlays
|
||||
*/
|
||||
overlayDisplaySets?: any[];
|
||||
/**
|
||||
* Display sets that could be toggled on as overlays (derived modalities)
|
||||
*/
|
||||
potentialOverlayDisplaySets?: any[];
|
||||
/**
|
||||
* Display sets that could be added as foreground layers
|
||||
*/
|
||||
potentialForegroundDisplaySets?: any[];
|
||||
/**
|
||||
* Display sets that could replace the current background
|
||||
*/
|
||||
potentialBackgroundDisplaySets?: any[];
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 [_, viewportGridService] = useViewportGrid();
|
||||
|
||||
// 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 = useMemo(
|
||||
() =>
|
||||
needsSegmentations ? segmentationService.getSegmentationRepresentations(viewportId) : [],
|
||||
[segmentationService, viewportId, 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(
|
||||
() => 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,
|
||||
services: { displaySetService, viewportGridService },
|
||||
});
|
||||
}, [viewportId, displaySetService, viewportGridService, needsEnhancedDisplaySets]);
|
||||
|
||||
const backgroundDisplaySet = useMemo(
|
||||
() => (includeBackground ? 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 = {};
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in new issue
Block a user