feat(data overlay): add foreground background control plus orientation change in the UI and add RTDOSE support (#5007)

This commit is contained in:
Alireza authored and GitHub committed 2025-05-05 16:35:05 -04:00
1 parent c1fced0573
commit cd4e6408bc
105 files changed
+3117 -788

No files matched your search

@@ -1,18 +1,18 @@
import React, { ReactElement, useCallback, useEffect, useState } from 'react';
import { Switch } from '@ohif/ui-next';
import { StackViewport, VolumeViewport } from '@cornerstonejs/core';
import { StackViewport } from '@cornerstonejs/core';
import { ColorbarProps } from '../../types/Colorbar';
import { utilities } from '@cornerstonejs/core';
import { useSystem } from '@ohif/core';
export function setViewportColorbar(
viewportId,
displaySets,
commandsManager,
servicesManager: AppTypes.ServicesManager,
colorbarOptions
) {
const { cornerstoneViewportService } = servicesManager.services;
const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
const { cornerstoneViewportService, viewportGridService } = servicesManager.services;
const displaySetInstanceUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
const backgroundColor = viewportInfo.getViewportOptions().background;
@@ -32,36 +32,18 @@ export function setViewportColorbar(
};
}
const displaySetInstanceUIDs = [];
if (viewport instanceof StackViewport) {
displaySetInstanceUIDs.push(viewportId);
}
if (viewport instanceof VolumeViewport) {
displaySets.forEach(ds => {
displaySetInstanceUIDs.push(ds.displaySetInstanceUID);
});
}
commandsManager.run({
commandName: 'toggleViewportColorbar',
commandOptions: {
viewportId,
options: colorbarOptions,
displaySetInstanceUIDs,
},
context: 'CORNERSTONE',
commandsManager.run('toggleViewportColorbar', {
viewportId,
options: colorbarOptions,
displaySetInstanceUIDs,
});
}
export function Colorbar({
viewportId,
displaySets,
commandsManager,
servicesManager,
colorbarProperties,
}: withAppTypes<ColorbarProps>): ReactElement {
const { servicesManager, commandsManager } = useSystem();
const { colorbarService } = servicesManager.services;
const {
width: colorbarWidth,
@@ -73,7 +55,7 @@ export function Colorbar({
const [showColorbar, setShowColorbar] = useState(colorbarService.hasColorbar(viewportId));
const onSetColorbar = useCallback(() => {
setViewportColorbar(viewportId, displaySets, commandsManager, servicesManager, {
setViewportColorbar(viewportId, commandsManager, servicesManager, {
viewportId,
colormaps,
ticks: {
@@ -3,14 +3,10 @@ import { AllInOneMenu, Switch, Tabs, TabsList, TabsTrigger } from '@ohif/ui-next
import { StackViewport, Types } from '@cornerstonejs/core';
import { ColormapProps } from '../../types/Colormap';
import { useSystem } from '@ohif/core';
export function Colormap({
colormaps,
viewportId,
displaySets,
commandsManager,
servicesManager,
}: ColormapProps): ReactElement {
export function Colormap({ colormaps, viewportId, displaySets }: ColormapProps): ReactElement {
const { servicesManager, commandsManager } = useSystem();
const { cornerstoneViewportService } = servicesManager.services;
const [activeDisplaySet, setActiveDisplaySet] = useState(displaySets[0]);
@@ -1,13 +1,10 @@
import React, { ReactElement, useState, useEffect, useCallback } from 'react';
import { VolumeLightingProps } from '../../types/ViewportPresets';
import { Numeric } from '@ohif/ui-next';
import { useSystem } from '@ohif/core';
export function VolumeLighting({
servicesManager,
commandsManager,
viewportId,
hasShade,
}: VolumeLightingProps): ReactElement {
export function VolumeLighting({ viewportId, hasShade }: VolumeLightingProps): ReactElement {
const { servicesManager, commandsManager } = useSystem();
const { cornerstoneViewportService } = servicesManager.services;
const [lightingValues, setLightingValues] = useState({
ambient: null,
@@ -5,43 +5,31 @@ import { VolumeRenderingQuality } from './VolumeRenderingQuality';
import { VolumeShift } from './VolumeShift';
import { VolumeLighting } from './VolumeLighting';
import { VolumeShade } from './VolumeShade';
export function VolumeRenderingOptions({
viewportId,
commandsManager,
volumeRenderingQualityRange,
servicesManager,
}: VolumeRenderingOptionsProps): ReactElement {
const [hasShade, setShade] = useState(false);
return (
<AllInOneMenu.ItemPanel>
<VolumeRenderingQuality
viewportId={viewportId}
commandsManager={commandsManager}
servicesManager={servicesManager}
volumeRenderingQualityRange={volumeRenderingQualityRange}
/>
<VolumeShift
viewportId={viewportId}
commandsManager={commandsManager}
servicesManager={servicesManager}
/>
<VolumeShift viewportId={viewportId} />
<div className="mt-2 flex h-8 !h-[20px] w-full flex-shrink-0 items-center justify-start px-2 text-base">
<div className="text-muted-foreground text-sm">Lighting</div>
</div>
<div className="bg-background mt-1 mb-1 h-px w-full"></div>
<div className="hover:bg-accent flex h-8 w-full flex-shrink-0 items-center px-2 text-base hover:rounded">
<VolumeShade
commandsManager={commandsManager}
servicesManager={servicesManager}
viewportId={viewportId}
onClickShade={setShade}
/>
</div>
<VolumeLighting
viewportId={viewportId}
commandsManager={commandsManager}
servicesManager={servicesManager}
hasShade={hasShade}
/>
</AllInOneMenu.ItemPanel>
@@ -3,13 +3,13 @@ import { Icons } from '@ohif/ui-next';
import React, { ReactElement } from 'react';
import { VolumeRenderingPresetsProps } from '../../types/ViewportPresets';
import { VolumeRenderingPresetsContent } from './VolumeRenderingPresetsContent';
import { useSystem } from '@ohif/core';
export function VolumeRenderingPresets({
viewportId,
servicesManager,
commandsManager,
volumeRenderingPresets,
}: VolumeRenderingPresetsProps): ReactElement {
const { servicesManager } = useSystem();
const { uiDialogService } = servicesManager.services;
const onClickPresets = () => {
@@ -21,7 +21,6 @@ export function VolumeRenderingPresets({
contentProps: {
presets: volumeRenderingPresets,
viewportId,
commandsManager,
},
});
};
@@ -1,13 +1,13 @@
import React, { ReactElement, useCallback, useState, useEffect } from 'react';
import { VolumeRenderingQualityProps } from '../../types/ViewportPresets';
import { Numeric } from '@ohif/ui-next';
import { useSystem } from '@ohif/core';
export function VolumeRenderingQuality({
volumeRenderingQualityRange,
commandsManager,
servicesManager,
viewportId,
}: VolumeRenderingQualityProps): ReactElement {
const { servicesManager, commandsManager } = useSystem();
const { cornerstoneViewportService } = servicesManager.services;
const { min, max, step } = volumeRenderingQualityRange;
const [quality, setQuality] = useState(null);
@@ -1,13 +1,13 @@
import React, { ReactElement, useCallback, useEffect, useState } from 'react';
import { Switch } from '@ohif/ui-next';
import { VolumeShadeProps } from '../../types/ViewportPresets';
import { useSystem } from '@ohif/core';
export function VolumeShade({
commandsManager,
viewportId,
servicesManager,
onClickShade = bool => {},
}: VolumeShadeProps): ReactElement {
const { servicesManager, commandsManager } = useSystem();
const { cornerstoneViewportService } = servicesManager.services;
const [shade, setShade] = useState(true);
const [key, setKey] = useState(0);
@@ -1,12 +1,10 @@
import React, { ReactElement, useCallback, useEffect, useState, useRef } from 'react';
import { VolumeShiftProps } from '../../types/ViewportPresets';
import { Numeric } from '@ohif/ui-next';
import { useSystem } from '@ohif/core';
export function VolumeShift({
viewportId,
commandsManager,
servicesManager,
}: VolumeShiftProps): ReactElement {
export function VolumeShift({ viewportId }: VolumeShiftProps): ReactElement {
const { servicesManager, commandsManager } = useSystem();
const { cornerstoneViewportService } = servicesManager.services;
const [minShift, setMinShift] = useState<number | null>(null);
const [maxShift, setMaxShift] = useState<number | null>(null);
@@ -1,20 +1,16 @@
import React, { ReactElement, useCallback } from 'react';
import { AllInOneMenu } from '@ohif/ui-next';
import { WindowLevelPreset } from '../../types/WindowLevel';
import { CommandsManager } from '@ohif/core';
import { useSystem } from '@ohif/core';
import { useTranslation } from 'react-i18next';
export type WindowLevelProps = {
viewportId: string;
presets: Array<Record<string, Array<WindowLevelPreset>>>;
commandsManager: CommandsManager;
};
export function WindowLevel({
viewportId,
commandsManager,
presets,
}: WindowLevelProps): ReactElement {
export function WindowLevel({ viewportId, presets }: WindowLevelProps): ReactElement {
const { commandsManager } = useSystem();
const { t } = useTranslation('WindowLevelActionMenu');
const onSetWindowLevel = useCallback(
@@ -15,8 +15,7 @@ import { VolumeRenderingOptions } from './VolumeRenderingOptions';
import { ViewportPreset } from '../../types/ViewportPresets';
import { VolumeViewport3D } from '@cornerstonejs/core';
import { utilities } from '@cornerstonejs/core';
export const nonWLModalities = ['SR', 'SEG', 'SM', 'RTSTRUCT', 'RTPLAN', 'RTDOSE'];
import { useSystem } from '@ohif/core';
export type WindowLevelActionMenuProps = {
viewportId: string;
@@ -34,13 +33,12 @@ export function WindowLevelActionMenu({
presets,
verticalDirection,
horizontalDirection,
commandsManager,
servicesManager,
colorbarProperties,
displaySets,
volumeRenderingPresets,
volumeRenderingQualityRange,
}: withAppTypes<WindowLevelActionMenuProps>): ReactElement {
const { commandsManager, servicesManager } = useSystem();
const {
colormaps,
colorbarContainerPosition,
@@ -48,6 +46,7 @@ export function WindowLevelActionMenu({
colorbarTickPosition,
width: colorbarWidth,
} = colorbarProperties;
const { colorbarService, cornerstoneViewportService } = servicesManager.services;
const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
@@ -64,7 +63,7 @@ export function WindowLevelActionMenu({
const [is3DVolume, setIs3DVolume] = useState(false);
const onSetColorbar = useCallback(() => {
setViewportColorbar(viewportId, displaySets, commandsManager, servicesManager, {
setViewportColorbar(viewportId, commandsManager, servicesManager, {
colormaps,
ticks: {
position: colorbarTickPosition,
@@ -117,7 +116,6 @@ export function WindowLevelActionMenu({
verticalDirection={verticalDirection}
horizontalDirection={horizontalDirection}
iconClassName={classNames(
// Visible on hover and for the active viewport
activeViewportId === viewportId ? 'visible' : 'invisible group-hover/pane:visible',
'flex shrink-0 cursor-pointer rounded active:text-foreground text-highlight',
isLight ? ' hover:bg-primary/30' : 'hover:bg-primary/30'
@@ -132,9 +130,7 @@ export function WindowLevelActionMenu({
{!is3DVolume && (
<Colorbar
viewportId={viewportId}
displaySets={displaySets.filter(ds => !nonWLModalities.includes(ds.Modality))}
commandsManager={commandsManager}
servicesManager={servicesManager}
displaySets={displaySets.filter(ds => ds.supportsWindowLevel === true)}
colorbarProperties={colorbarProperties}
/>
)}
@@ -150,9 +146,7 @@ export function WindowLevelActionMenu({
className="flex h-full w-full flex-col"
colormaps={colormaps}
viewportId={viewportId}
displaySets={displaySets.filter(ds => !nonWLModalities.includes(ds.Modality))}
commandsManager={commandsManager}
servicesManager={servicesManager}
displaySets={displaySets.filter(ds => ds.supportsWindowLevel === true)}
/>
</AllInOneMenu.SubMenu>
)}
@@ -165,7 +159,6 @@ export function WindowLevelActionMenu({
>
<WindowLevel
viewportId={viewportId}
commandsManager={commandsManager}
presets={presets}
/>
</AllInOneMenu.SubMenu>
@@ -173,9 +166,7 @@ export function WindowLevelActionMenu({
{volumeRenderingPresets && is3DVolume && (
<VolumeRenderingPresets
servicesManager={servicesManager}
viewportId={viewportId}
commandsManager={commandsManager}
volumeRenderingPresets={volumeRenderingPresets}
/>
)}
@@ -184,9 +175,7 @@ export function WindowLevelActionMenu({
<AllInOneMenu.SubMenu itemLabel="Rendering Options">
<VolumeRenderingOptions
viewportId={viewportId}
commandsManager={commandsManager}
volumeRenderingQualityRange={volumeRenderingQualityRange}
servicesManager={servicesManager}
/>
</AllInOneMenu.SubMenu>
)}
@@ -1,12 +1,12 @@
import React, { ReactNode } from 'react';
import { nonWLModalities } from './WindowLevelActionMenu';
import { WindowLevelActionMenuProps } from './WindowLevelActionMenu';
import { useSystem } from '@ohif/core';
import { WindowLevelActionMenu as WindowLevelActionMenuComponent } from './WindowLevelActionMenu';
export function getWindowLevelActionMenu({
function WindowLevelActionMenu({
viewportId,
element,
displaySets,
servicesManager,
commandsManager,
verticalDirection,
horizontalDirection,
}: withAppTypes<{
@@ -14,31 +14,25 @@ export function getWindowLevelActionMenu({
element: HTMLElement;
displaySets: AppTypes.DisplaySet[];
}>): ReactNode {
const { servicesManager } = useSystem();
const { customizationService } = servicesManager.services;
const presets = customizationService.getCustomization('cornerstone.windowLevelPresets');
const colorbarProperties = customizationService.getCustomization('cornerstone.colorbar');
const { volumeRenderingPresets, volumeRenderingQualityRange } =
customizationService.getCustomization('cornerstone.3dVolumeRendering');
const WindowLevelActionMenu = customizationService.getCustomization(
'viewportActionMenu.windowLevelActionMenu'
);
const displaySetPresets = displaySets
.filter(displaySet => presets[displaySet.Modality])
.map(displaySet => {
return { [displaySet.Modality]: presets[displaySet.Modality] };
});
const modalities = displaySets
.map(displaySet => displaySet.Modality)
.filter(modality => !nonWLModalities.includes(modality));
const modalities = displaySets.map(displaySet => displaySet.supportsWindowLevel);
if (modalities.length === 0) {
return null;
}
const WindowLevelActionMenuComponent = WindowLevelActionMenu?.component;
return (
<WindowLevelActionMenuComponent
viewportId={viewportId}
@@ -46,8 +40,6 @@ export function getWindowLevelActionMenu({
presets={displaySetPresets}
verticalDirection={verticalDirection}
horizontalDirection={horizontalDirection}
commandsManager={commandsManager}
servicesManager={servicesManager}
colorbarProperties={colorbarProperties}
displaySets={displaySets}
volumeRenderingPresets={volumeRenderingPresets}
@@ -55,3 +47,7 @@ export function getWindowLevelActionMenu({
/>
);
}
export function getWindowLevelActionMenu(props: WindowLevelActionMenuProps) {
return <WindowLevelActionMenu {...props} />;
}