Compare commits

...

1 Commits

Author SHA1 Message Date
mozzie
779e1383c6 test: try another way 2024-09-17 15:14:45 +08:00
5 changed files with 415 additions and 141 deletions

View File

@ -0,0 +1,185 @@
import * as cornerstoneTools from '@cornerstonejs/tools';
import type { Types } from '@cornerstonejs/tools';
const {
LengthTool,
StackScrollMouseWheelTool,
StackScrollTool,
PanTool,
ZoomTool,
TrackballRotateTool,
Enums: csToolsEnums,
} = cornerstoneTools;
const { MouseBindings, KeyboardBindings } = csToolsEnums;
let registered = false;
export type ToolBinding = {
// A base tool to register. Should only be defined once per tool
tool?: any;
// The tool name to base this on
baseTool?: string;
// The configuration to register with
configuration?: Record<string, any>;
// Sets to passive initially
passive?: boolean;
// Initial bindings
bindings?: Types.IToolBinding[];
};
/**
* Adds navigation bindings to the given tool group. Registers the basic
* tool with CS Tools if register is true.
*
* Adds:
* * Pan on Right or Primary+Ctrl
* * Zoom on Middle, Primary+Shift
* * Stack Scroll on Mouse Wheel, Primary+Alt
* * Length Tool on fourth button
*
* Also allows registering other tools by having them in the options.toolMap with configuration values.
*/
export default function addManipulationBindings(
toolGroup,
options: {
enableShiftClickZoom?: boolean;
is3DViewport?: boolean;
toolMap?: Map<string, ToolBinding>;
} = {}
) {
const zoomBindings: Types.IToolBinding[] = [
{
mouseButton: MouseBindings.Secondary,
},
];
const {
is3DViewport = false,
enableShiftClickZoom = false,
toolMap = new Map(),
} = options;
if (enableShiftClickZoom === true) {
zoomBindings.push({
mouseButton: MouseBindings.Primary, // Shift Left Click
modifierKey: KeyboardBindings.Shift,
});
}
if (!registered) {
cornerstoneTools.addTool(StackScrollMouseWheelTool);
cornerstoneTools.addTool(PanTool);
cornerstoneTools.addTool(ZoomTool);
cornerstoneTools.addTool(TrackballRotateTool);
cornerstoneTools.addTool(LengthTool);
cornerstoneTools.addTool(StackScrollTool);
for (const [, config] of toolMap) {
if (config.tool) {
cornerstoneTools.addTool(config.tool);
}
}
}
registered = true;
toolGroup.addTool(PanTool.toolName);
// Allow significant zooming to occur
toolGroup.addTool(ZoomTool.toolName, {
minZoomScale: 0.001,
maxZoomScale: 4000,
});
if (is3DViewport) {
toolGroup.addTool(TrackballRotateTool.toolName);
} else {
toolGroup.addTool(StackScrollMouseWheelTool.toolName);
}
toolGroup.addTool(LengthTool.toolName);
toolGroup.addTool(StackScrollTool.toolName);
toolGroup.setToolActive(PanTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Auxiliary,
},
{
numTouchPoints: 1,
modifierKey: KeyboardBindings.Ctrl,
},
],
});
toolGroup.setToolActive(ZoomTool.toolName, {
bindings: zoomBindings,
});
// Need a binding to navigate without a wheel mouse
toolGroup.setToolActive(StackScrollTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Primary,
modifierKey: KeyboardBindings.Alt,
},
{
numTouchPoints: 1,
modifierKey: KeyboardBindings.Alt,
},
],
});
// Add a length tool binding to allow testing annotations on examples targetting
// other use cases. Use a primary button with shift+ctrl as that is relatively
// unlikely to be otherwise used.
toolGroup.setToolActive(LengthTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Primary,
modifierKey: KeyboardBindings.ShiftCtrl,
},
{
numTouchPoints: 1,
modifierKey: KeyboardBindings.ShiftCtrl,
},
],
});
if (is3DViewport) {
toolGroup.setToolActive(TrackballRotateTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Primary,
},
],
});
} else {
toolGroup.setToolActive(StackScrollMouseWheelTool.toolName);
}
// Add extra tools from the toolMap
for (const [toolName, config] of toolMap) {
if (config.baseTool) {
if (!toolGroup.hasTool(config.baseTool)) {
toolGroup.addTool(
config.baseTool,
toolMap.get(config.baseTool)?.configuration
);
}
toolGroup.addToolInstance(
toolName,
config.baseTool,
config.configuration
);
} else if (!toolGroup.hasTool(toolName)) {
toolGroup.addTool(toolName, config.configuration);
}
if (config.passive) {
// This can be applied during add/remove contours
toolGroup.setToolPassive(toolName);
}
if (config.bindings || config.selected) {
toolGroup.setToolActive(
toolName,
(config.bindings && config) || {
bindings: [{ mouseButton: MouseBindings.Primary }],
}
);
}
}
}

View File

@ -1,5 +1,11 @@
type VOIRange = {
/** upper value for display */
upper: number;
/** lower value for display */
lower: number;
};
let ctVoiRange; let ctVoiRange: VOIRange
export interface CtTransferFunction { export interface CtTransferFunction {
volumeActor: any volumeActor: any

View File

@ -6,6 +6,7 @@ import {
RenderingEngine, RenderingEngine,
setVolumesForViewports, setVolumesForViewports,
Enums as CoreEnums, Enums as CoreEnums,
volumeLoader,
} from "@cornerstonejs/core"; } from "@cornerstonejs/core";
import { import {
viewportColors, viewportColors,
@ -18,6 +19,8 @@ import {
ViewportId, ViewportId,
} from "./Crosshair.config"; } from "./Crosshair.config";
const viewportIds = [viewportId1, viewportId2, viewportId3];
const { const {
ToolGroupManager, ToolGroupManager,
CrosshairsTool, CrosshairsTool,
@ -55,7 +58,7 @@ interface CrosshairMprProps {
windowCenter: number; windowCenter: number;
windowWidth: number; windowWidth: number;
}; };
volumeId: string; imageIds: string[];
} }
export const CrosshairMpr = (props: CrosshairMprProps) => { export const CrosshairMpr = (props: CrosshairMprProps) => {
@ -64,32 +67,23 @@ export const CrosshairMpr = (props: CrosshairMprProps) => {
const viewportRef_SAGITTAL = useRef<HTMLDivElement | null>(null); const viewportRef_SAGITTAL = useRef<HTMLDivElement | null>(null);
const viewportRef_CORONAL = useRef<HTMLDivElement | null>(null); const viewportRef_CORONAL = useRef<HTMLDivElement | null>(null);
const renderingEngine = useRef<RenderingEngine>(); const renderingEngine = useRef<RenderingEngine>();
const toolGroupRef = useRef<cornerstoneTools.Types.IToolGroup | undefined>( const volumeId = "volumeId";
undefined const toolGroupId = "mprToolGroup";
); const renderingEngineId = "mprRenderingEngine";
const ts = "-" + Date.now();
const toolGroupId = "mprToolGroup" + ts;
const renderingEngineId = "mprRenderingEngine" + ts;
useEffect(() => { useEffect(() => {
cornerstoneTools.addTool(StackScrollMouseWheelTool);
cornerstoneTools.addTool(CrosshairsTool);
cornerstoneTools.addTool(WindowLevelTool);
cornerstoneTools.addTool(ZoomTool);
toolGroupRef.current = ToolGroupManager.createToolGroup(toolGroupId);
const run = async () => { const run = async () => {
if ( if (
!viewportRef_AXIAL.current || viewportRef_AXIAL.current &&
!viewportRef_SAGITTAL.current || viewportRef_SAGITTAL.current &&
!viewportRef_CORONAL.current viewportRef_CORONAL.current
) ) {
return; console.log("mpr rendering");
renderingEngine.current = new RenderingEngine(renderingEngineId); renderingEngine.current = new RenderingEngine(renderingEngineId);
// Create the viewports
const viewportInputArray: PublicViewportInput[] = [ const viewportInputArray: PublicViewportInput[] = [
{ {
viewportId: viewportId1, viewportId: viewportId1,
@ -119,15 +113,16 @@ export const CrosshairMpr = (props: CrosshairMprProps) => {
}, },
}, },
]; ];
renderingEngine.current.setViewports(viewportInputArray); renderingEngine.current.setViewports(viewportInputArray);
// Set volumes on the viewports /**
* bug here: WebGL: INVALID_OPERATION: bindTexture: object does not belong to this context
*/
await setVolumesForViewports( await setVolumesForViewports(
renderingEngine.current, renderingEngine.current,
[ [
{ {
volumeId: props.volumeId, volumeId: volumeId,
callback: ({ volumeActor }) => callback: ({ volumeActor }) =>
setCtTransferFunctionForVolumeActor({ setCtTransferFunctionForVolumeActor({
volumeActor, volumeActor,
@ -136,50 +131,41 @@ export const CrosshairMpr = (props: CrosshairMprProps) => {
}), }),
}, },
], ],
[viewportId1, viewportId2, viewportId3] viewportIds
); );
if (toolGroupRef.current) { const toolGroup = ToolGroupManager.createToolGroup(toolGroupId);
// For the crosshairs to operate, the viewports must currently be
// added ahead of setting the tool active. This will be improved in the future.
toolGroupRef.current.addViewport(viewportId1, renderingEngineId);
toolGroupRef.current.addViewport(viewportId2, renderingEngineId);
toolGroupRef.current.addViewport(viewportId3, renderingEngineId);
/** if (toolGroup) {
* zoom影像 // 为使十字准线正常工作,目前必须在将工具设置为活动之前添加视口
*/ toolGroup.addViewport(viewportId1, renderingEngineId);
toolGroupRef.current.addTool(ZoomTool.toolName); toolGroup.addViewport(viewportId2, renderingEngineId);
toolGroupRef.current.setToolActive(ZoomTool.toolName, { toolGroup.addViewport(viewportId3, renderingEngineId);
toolGroup.addTool(ZoomTool.toolName);
toolGroup.addTool(StackScrollMouseWheelTool.toolName);
// 添加十字准线工具并将其配置为连接三个视口
toolGroup.addTool(CrosshairsTool.toolName, {
getReferenceLineColor,
getReferenceLineControllable,
getReferenceLineDraggableRotatable,
getReferenceLineSlabThicknessControlsOn,
});
toolGroup.addTool(WindowLevelTool.toolName);
toolGroup.setToolActive(CrosshairsTool.toolName, {
bindings: [{ mouseButton: 1 }],
});
toolGroup.setToolActive(ZoomTool.toolName, {
bindings: [ bindings: [
{ {
mouseButton: MouseBindings.Secondary, // 鼠标中键 mouseButton: MouseBindings.Secondary, // 鼠标中键
}, },
], ],
}); });
// using the `mouseWheelCallback`
// Manipulation Tools toolGroup.setToolActive(StackScrollMouseWheelTool.toolName);
toolGroupRef.current.addTool(StackScrollMouseWheelTool.toolName); toolGroup.setToolActive(WindowLevelTool.toolName, {
// Add Crosshairs tool and configure it to link the three viewports
// These viewports could use different tool groups. See the PET-CT example
// for a more complicated used case.
toolGroupRef.current.addTool(CrosshairsTool.toolName, {
getReferenceLineColor,
getReferenceLineControllable,
getReferenceLineDraggableRotatable,
getReferenceLineSlabThicknessControlsOn,
});
toolGroupRef.current.setToolActive(CrosshairsTool.toolName, {
bindings: [{ mouseButton: 1 }],
});
// As the Stack Scroll mouse wheel is a tool using the `mouseWheelCallback`
// hook instead of mouse buttons, it does not need to assign any mouse button.
toolGroupRef.current.setToolActive(StackScrollMouseWheelTool.toolName);
toolGroupRef.current.addTool(WindowLevelTool.toolName);
toolGroupRef.current.setToolActive(WindowLevelTool.toolName, {
bindings: [ bindings: [
{ {
mouseButton: MouseBindings.Auxiliary, mouseButton: MouseBindings.Auxiliary,
@ -188,32 +174,22 @@ export const CrosshairMpr = (props: CrosshairMprProps) => {
}); });
} }
renderingEngine.current.renderViewports([ renderingEngine.current.renderViewports(viewportIds);
viewportId1, }
viewportId2,
viewportId3,
]);
}; };
run(); run();
return () => { return () => {
// 禁用视口
renderingEngine.current?.disableElement(viewportId1); renderingEngine.current?.disableElement(viewportId1);
renderingEngine.current?.disableElement(viewportId2); renderingEngine.current?.disableElement(viewportId2);
renderingEngine.current?.disableElement(viewportId3); renderingEngine.current?.disableElement(viewportId3);
// 销毁渲染引擎
renderingEngine.current?.destroy();
// 从 ToolGroupManager 中移除工具组 // 从 ToolGroupManager 中移除工具组
ToolGroupManager.destroyToolGroup(toolGroupId); ToolGroupManager.destroyToolGroup(toolGroupId);
// 移出工具注册 // 销毁渲染引擎
cornerstoneTools.removeTool(StackScrollMouseWheelTool); renderingEngine.current?.destroy();
cornerstoneTools.removeTool(CrosshairsTool);
cornerstoneTools.removeTool(WindowLevelTool);
cornerstoneTools.removeTool(ZoomTool);
}; };
}, [props, renderingEngineId, toolGroupId]); }, [props, renderingEngineId, toolGroupId]);
@ -238,8 +214,14 @@ export const CrosshairMpr = (props: CrosshairMprProps) => {
return ( return (
<div ref={containerRef} className="w-full h-full flex flex-col"> <div ref={containerRef} className="w-full h-full flex flex-col">
<div className="w-full h-1/3" ref={viewportRef_AXIAL} /> <div
<div className="w-full h-1/3" ref={viewportRef_SAGITTAL} /> className="w-full h-1/3 border-b border-secondary"
ref={viewportRef_AXIAL}
/>
<div
className="w-full h-1/3 border-b border-secondary"
ref={viewportRef_SAGITTAL}
/>
<div className="w-full h-1/3" ref={viewportRef_CORONAL} /> <div className="w-full h-1/3" ref={viewportRef_CORONAL} />
</div> </div>
); );

View File

@ -1,22 +1,44 @@
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import * as cornerstoneTools from "@cornerstonejs/tools";
import { PublicViewportInput } from "@cornerstonejs/core/dist/types/types/IViewport.js"; import { PublicViewportInput } from "@cornerstonejs/core/dist/types/types/IViewport.js";
import { Enums as CoreEnums, RenderingEngine } from "@cornerstonejs/core"; import { Enums as CoreEnums, RenderingEngine } from "@cornerstonejs/core";
import { IStackViewport } from "@cornerstonejs/core/dist/types/types"; import { IStackViewport } from "@cornerstonejs/core/dist/types/types";
import { Slider } from "@/components/ui/slider"; import { Slider } from "@/components/ui/slider";
import { ctVoiRange } from "../MprViewer/CornerstoneDicomLoader/setCtTransferFunctionForVolumeActor";
const {
ToolGroupManager,
WindowLevelTool,
ZoomTool,
Enums: csToolsEnums,
} = cornerstoneTools;
const { MouseBindings } = csToolsEnums;
export interface StackViewerProps { export interface StackViewerProps {
imageIds: string[]; imageIds: string[];
wwwl: {
windowCenter: number;
windowWidth: number;
};
} }
export const StackViewer = (props: StackViewerProps) => { export const StackViewer = (props: StackViewerProps) => {
const viewportStackRef = useRef<HTMLDivElement | null>(null); const viewportStackRef = useRef<HTMLDivElement | null>(null);
const renderingEngineRef = useRef<RenderingEngine>(); const renderingEngineRef = useRef<RenderingEngine>();
/**
* index
*/
const [index, setIndex] = useState(Math.floor(props.imageIds.length / 2));
const containerRef = useRef<HTMLDivElement | null>(null); const containerRef = useRef<HTMLDivElement | null>(null);
const viewportId = "stackViewport"; const viewportId = "stackViewport";
const renderingEngineId = "stackRenderingEngine"; const renderingEngineId = "stackRenderingEngine";
const toolGroupId = "stackToolGroup";
useEffect(() => { useEffect(() => {
if (!viewportStackRef.current) return; if (!viewportStackRef.current) return;
const toolGroup = ToolGroupManager.createToolGroup(toolGroupId);
renderingEngineRef.current = new RenderingEngine(renderingEngineId); renderingEngineRef.current = new RenderingEngine(renderingEngineId);
const viewportInput: PublicViewportInput = { const viewportInput: PublicViewportInput = {
viewportId, viewportId,
@ -32,13 +54,54 @@ export const StackViewer = (props: StackViewerProps) => {
) as IStackViewport; ) as IStackViewport;
viewport.setStack(props.imageIds); viewport.setStack(props.imageIds);
// viewport.setProperties({
// voiRange: ctVoiRange,
// });
if (toolGroup) {
toolGroup.addViewport(viewportId, renderingEngineId);
toolGroup.addTool(ZoomTool.toolName);
toolGroup.setToolActive(ZoomTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Secondary, // 鼠标中键
},
],
});
toolGroup.addTool(WindowLevelTool.toolName);
toolGroup.setToolActive(WindowLevelTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Auxiliary,
},
],
});
}
return () => { return () => {
ToolGroupManager.destroyToolGroup(toolGroupId);
renderingEngineRef.current?.disableElement(viewportId); renderingEngineRef.current?.disableElement(viewportId);
renderingEngineRef.current?.destroy(); renderingEngineRef.current?.destroy();
}; };
}, [props.imageIds, renderingEngineId, viewportId]); }, [props.imageIds, renderingEngineId, viewportId]);
/**
*
*/
useEffect(() => {
const handleWheel = (event: WheelEvent) => {
const delta = event.deltaY > 0 ? -1 : 1;
if (delta === -1 && index === 0) return;
if (delta === 1 && index === props.imageIds.length - 1) return;
setIndex((p) => p + delta);
};
const stackElement = viewportStackRef.current;
stackElement?.addEventListener("wheel", handleWheel);
return () => {
stackElement?.removeEventListener("wheel", handleWheel);
};
}, [index, props.imageIds.length]);
useEffect(() => { useEffect(() => {
const container = containerRef.current; const container = containerRef.current;
if (!container) return; if (!container) return;
@ -58,22 +121,38 @@ export const StackViewer = (props: StackViewerProps) => {
}; };
}, []); }, []);
/**
*
*/
const onChangeIndex = (value: number[]) => { const onChangeIndex = (value: number[]) => {
if (renderingEngineRef.current) { setIndex(value[0]);
const viewport = renderingEngineRef.current.getViewport(viewportId);
(viewport as IStackViewport).setImageIdIndex(value[0]);
}
}; };
useEffect(() => {
if (renderingEngineRef.current) {
const viewport = renderingEngineRef.current.getViewport(viewportId);
(viewport as IStackViewport).setImageIdIndex(index);
}
}, [index]);
return ( return (
<div ref={containerRef} className="w-full h-full flex flex-col pb-4"> <div
ref={containerRef}
className="relative w-full h-full flex flex-col pb-8"
>
<div className="w-full h-full" ref={viewportStackRef} /> <div className="w-full h-full" ref={viewportStackRef} />
<div className="absolute w-1/2 left-1/2 transform -translate-x-1/2 bottom-4">
<div>
{index + 1}/{props.imageIds.length}
</div>
<Slider <Slider
defaultValue={[0]} value={[index]}
min={0}
max={props.imageIds.length - 1} max={props.imageIds.length - 1}
step={1} step={1}
onValueChange={onChangeIndex} onValueChange={onChangeIndex}
/> />
</div> </div>
</div>
); );
}; };

View File

@ -9,35 +9,42 @@ import {
} from "@/components/ui/resizable"; } from "@/components/ui/resizable";
import { StackViewer } from "./StackViewer"; import { StackViewer } from "./StackViewer";
import { createImageIdsAndCacheMetaData } from "./MprViewer/CornerstoneDicomLoader/createImageIdsAndCacheMetaData"; import { createImageIdsAndCacheMetaData } from "./MprViewer/CornerstoneDicomLoader/createImageIdsAndCacheMetaData";
import * as cornerstoneTools from "@cornerstonejs/tools";
import { volumeLoader } from "@cornerstonejs/core"; import { volumeLoader } from "@cornerstonejs/core";
const wadoRsRoot = "http://localhost:8042/dicom-web"; const wadoRsRoot = "http://localhost:8042/dicom-web";
const { StackScrollMouseWheelTool, WindowLevelTool, ZoomTool, CrosshairsTool } =
cornerstoneTools;
export const Viewer = () => { export const Viewer = () => {
const [cornerstoneLoaded, setCornerstoneLoaded] = useState(false); const [cornerstoneLoaded, setCornerstoneLoaded] = useState(false);
const location = useLocation(); const location = useLocation();
const queryParams = new URLSearchParams(location.search); const queryParams = new URLSearchParams(location.search);
const SeriesInstanceUID = queryParams.get("SeriesInstanceUID"); const SeriesInstanceUID = queryParams.get("SeriesInstanceUID");
const StudyInstanceUID = queryParams.get("StudyInstanceUID"); const StudyInstanceUID = queryParams.get("StudyInstanceUID");
const imageIdsRef = useRef<string[]>(); const imageIdsSorted = useRef<string[]>();
const volumeId = "volume";
const wwwl = { windowCenter: 50, windowWidth: 850 }; const wwwl = { windowCenter: 50, windowWidth: 850 };
const volumeId = "volumeId";
useEffect(() => { useEffect(() => {
const setImageOrderCache = async () => { const setImageOrderCache = async () => {
if (!StudyInstanceUID || !SeriesInstanceUID) return; if (!StudyInstanceUID || !SeriesInstanceUID) return;
// imageIds此时由于流式加载for mpr是错乱的图片顺序 // imageIds此时由于流式加载for mpr是错乱的图片顺序
const imageIds = await createImageIdsAndCacheMetaData({ const unSortedImageIds = await createImageIdsAndCacheMetaData({
StudyInstanceUID, StudyInstanceUID,
SeriesInstanceUID, SeriesInstanceUID,
wadoRsRoot, wadoRsRoot,
}); });
// 这一步会对imageIds进行排序如果不排序imageIds会错误乱,stackViewport顺序会错误 // 这一步会对imageIds进行排序如果不排序imageIds会错误乱,stackViewport顺序会错误
const volume = await volumeLoader.createAndCacheVolume(volumeId, { const volume = await volumeLoader.createAndCacheVolume(volumeId, {
imageIds, imageIds: unSortedImageIds,
}); });
volume.load(); volume.load();
imageIdsRef.current = volume.imageIds;
imageIdsSorted.current = volume.imageIds;
// 默认windowWidtth // 默认windowWidtth
const { windowCenter, windowWidth } = volume.cornerstoneImageMetaData; const { windowCenter, windowWidth } = volume.cornerstoneImageMetaData;
@ -48,8 +55,20 @@ export const Viewer = () => {
initCornerstone(() => { initCornerstone(() => {
setImageOrderCache(); setImageOrderCache();
cornerstoneTools.addTool(StackScrollMouseWheelTool);
cornerstoneTools.addTool(WindowLevelTool);
cornerstoneTools.addTool(ZoomTool);
cornerstoneTools.addTool(CrosshairsTool);
}); });
return () => {
// 移出工具注册
cornerstoneTools.removeTool(CrosshairsTool);
cornerstoneTools.removeTool(StackScrollMouseWheelTool);
cornerstoneTools.removeTool(WindowLevelTool);
cornerstoneTools.removeTool(ZoomTool);
};
}, [SeriesInstanceUID, StudyInstanceUID]); }, [SeriesInstanceUID, StudyInstanceUID]);
return ( return (
@ -67,8 +86,11 @@ export const Viewer = () => {
> >
<ResizablePanel defaultSize={50}> <ResizablePanel defaultSize={50}>
<div className="w-full h-full"> <div className="w-full h-full">
{cornerstoneLoaded && imageIdsRef.current && ( {cornerstoneLoaded && imageIdsSorted.current && (
<StackViewer imageIds={imageIdsRef.current} /> <StackViewer
imageIds={imageIdsSorted.current}
wwwl={wwwl}
/>
)} )}
</div> </div>
</ResizablePanel> </ResizablePanel>
@ -81,8 +103,8 @@ export const Viewer = () => {
</ResizablePanel> </ResizablePanel>
<ResizableHandle withHandle /> <ResizableHandle withHandle />
<ResizablePanel defaultSize={50}> <ResizablePanel defaultSize={50}>
{cornerstoneLoaded && imageIdsRef.current && ( {cornerstoneLoaded && imageIdsSorted.current && (
<CrosshairMpr wwwl={wwwl} volumeId={volumeId} /> <CrosshairMpr wwwl={wwwl} imageIds={imageIdsSorted.current} />
)} )}
</ResizablePanel> </ResizablePanel>
</ResizablePanelGroup> </ResizablePanelGroup>