From 039325b7689a429aa760461e54c5dfd63131070c Mon Sep 17 00:00:00 2001 From: mozzie Date: Wed, 18 Sep 2024 10:12:47 +0800 Subject: [PATCH] feat: integration version --- .../src/pages/Viewer/ModelViewer/index.tsx | 3 + .../src/pages/Viewer/MprViewer/Crosshair.tsx | 246 --------------- ...{Crosshair.config.tsx => index.config.tsx} | 20 ++ .../pages/Viewer/StackViewer/index.config.ts | 2 + .../src/pages/Viewer/StackViewer/index.tsx | 79 ----- .../{MprViewer => }/ToolBarMenu/index.tsx | 0 apps/desktop/src/pages/Viewer/index.tsx | 284 ++++++++++++++++-- 7 files changed, 288 insertions(+), 346 deletions(-) create mode 100644 apps/desktop/src/pages/Viewer/ModelViewer/index.tsx delete mode 100644 apps/desktop/src/pages/Viewer/MprViewer/Crosshair.tsx rename apps/desktop/src/pages/Viewer/MprViewer/{Crosshair.config.tsx => index.config.tsx} (63%) create mode 100644 apps/desktop/src/pages/Viewer/StackViewer/index.config.ts delete mode 100644 apps/desktop/src/pages/Viewer/StackViewer/index.tsx rename apps/desktop/src/pages/Viewer/{MprViewer => }/ToolBarMenu/index.tsx (100%) diff --git a/apps/desktop/src/pages/Viewer/ModelViewer/index.tsx b/apps/desktop/src/pages/Viewer/ModelViewer/index.tsx new file mode 100644 index 0000000..9c9e16c --- /dev/null +++ b/apps/desktop/src/pages/Viewer/ModelViewer/index.tsx @@ -0,0 +1,3 @@ +export const Model3DViewer = () => { + return
3d model
; +}; diff --git a/apps/desktop/src/pages/Viewer/MprViewer/Crosshair.tsx b/apps/desktop/src/pages/Viewer/MprViewer/Crosshair.tsx deleted file mode 100644 index 87d9a80..0000000 --- a/apps/desktop/src/pages/Viewer/MprViewer/Crosshair.tsx +++ /dev/null @@ -1,246 +0,0 @@ -import { useEffect, useRef } from "react"; -import * as cornerstoneTools from "@cornerstonejs/tools"; -import { PublicViewportInput } from "@cornerstonejs/core/dist/types/types/IViewport.js"; -import setCtTransferFunctionForVolumeActor from "./CornerstoneDicomLoader/setCtTransferFunctionForVolumeActor"; -import { - RenderingEngine, - setVolumesForViewports, - Enums as CoreEnums, -} from "@cornerstonejs/core"; -import { - viewportColors, - viewportReferenceLineControllable, - viewportReferenceLineDraggableRotatable, - viewportReferenceLineSlabThicknessControlsOn, - viewportId1, - viewportId2, - viewportId3, - ViewportId, -} from "./Crosshair.config"; - -const { - ToolGroupManager, - CrosshairsTool, - StackScrollMouseWheelTool, - WindowLevelTool, - ZoomTool, - Enums: csToolsEnums, -} = cornerstoneTools; - -const { MouseBindings } = csToolsEnums; - -const { ViewportType } = CoreEnums; - -function getReferenceLineColor(vpId: ViewportId) { - return viewportColors[vpId]; -} - -function getReferenceLineControllable(vpId: ViewportId) { - const index = viewportReferenceLineControllable.indexOf(vpId); - return index !== -1; -} - -function getReferenceLineDraggableRotatable(vpId: ViewportId) { - const index = viewportReferenceLineDraggableRotatable.indexOf(vpId); - return index !== -1; -} - -function getReferenceLineSlabThicknessControlsOn(vpId: ViewportId) { - const index = viewportReferenceLineSlabThicknessControlsOn.indexOf(vpId); - return index !== -1; -} - -interface CrosshairMprProps { - wwwl: { - windowCenter: number; - windowWidth: number; - }; - volumeId: string; -} - -export const CrosshairMpr = (props: CrosshairMprProps) => { - const containerRef = useRef(null); - const viewportRef_AXIAL = useRef(null); - const viewportRef_SAGITTAL = useRef(null); - const viewportRef_CORONAL = useRef(null); - const renderingEngine = useRef(); - const toolGroupRef = useRef( - undefined - ); - const ts = "-" + Date.now(); - const toolGroupId = "mprToolGroup" + ts; - const renderingEngineId = "mprRenderingEngine" + ts; - - useEffect(() => { - cornerstoneTools.addTool(StackScrollMouseWheelTool); - cornerstoneTools.addTool(CrosshairsTool); - cornerstoneTools.addTool(WindowLevelTool); - cornerstoneTools.addTool(ZoomTool); - - toolGroupRef.current = ToolGroupManager.createToolGroup(toolGroupId); - - const run = async () => { - if ( - !viewportRef_AXIAL.current || - !viewportRef_SAGITTAL.current || - !viewportRef_CORONAL.current - ) - return; - - renderingEngine.current = new RenderingEngine(renderingEngineId); - - // Create the viewports - const viewportInputArray: PublicViewportInput[] = [ - { - viewportId: viewportId1, - type: ViewportType.ORTHOGRAPHIC, - element: viewportRef_AXIAL.current, - defaultOptions: { - orientation: CoreEnums.OrientationAxis.AXIAL, - background: [0, 0, 0], - }, - }, - { - viewportId: viewportId2, - type: ViewportType.ORTHOGRAPHIC, - element: viewportRef_SAGITTAL.current, - defaultOptions: { - orientation: CoreEnums.OrientationAxis.SAGITTAL, - background: [0, 0, 0], - }, - }, - { - viewportId: viewportId3, - type: ViewportType.ORTHOGRAPHIC, - element: viewportRef_CORONAL.current, - defaultOptions: { - orientation: CoreEnums.OrientationAxis.CORONAL, - background: [0, 0, 0], - }, - }, - ]; - - renderingEngine.current.setViewports(viewportInputArray); - - // Set volumes on the viewports - await setVolumesForViewports( - renderingEngine.current, - [ - { - volumeId: props.volumeId, - callback: ({ volumeActor }) => - setCtTransferFunctionForVolumeActor({ - volumeActor, - defaultWindowCenter: props.wwwl.windowCenter, - defaultWindowWidth: props.wwwl.windowWidth, - }), - }, - ], - [viewportId1, viewportId2, viewportId3] - ); - - if (toolGroupRef.current) { - // 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); - - /** - * zoom影像 - */ - toolGroupRef.current.addTool(ZoomTool.toolName); - toolGroupRef.current.setToolActive(ZoomTool.toolName, { - bindings: [ - { - mouseButton: MouseBindings.Secondary, // 鼠标中键 - }, - ], - }); - - // Manipulation Tools - toolGroupRef.current.addTool(StackScrollMouseWheelTool.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: [ - { - mouseButton: MouseBindings.Auxiliary, - }, - ], - }); - } - - renderingEngine.current.renderViewports([ - viewportId1, - viewportId2, - viewportId3, - ]); - }; - - run(); - - return () => { - // 禁用视口 - renderingEngine.current?.disableElement(viewportId1); - renderingEngine.current?.disableElement(viewportId2); - renderingEngine.current?.disableElement(viewportId3); - - // 销毁渲染引擎 - renderingEngine.current?.destroy(); - - // 从 ToolGroupManager 中移除工具组 - ToolGroupManager.destroyToolGroup(toolGroupId); - - // 移出工具注册 - cornerstoneTools.removeTool(StackScrollMouseWheelTool); - cornerstoneTools.removeTool(CrosshairsTool); - cornerstoneTools.removeTool(WindowLevelTool); - cornerstoneTools.removeTool(ZoomTool); - }; - }, [props, renderingEngineId, toolGroupId]); - - /** - * mpr resize - */ - useEffect(() => { - const container = containerRef.current; - if (!container) return; - let resizeTimeout: NodeJS.Timeout | null = null; - const resizeObserver = new ResizeObserver(() => { - if (resizeTimeout) clearTimeout(resizeTimeout); - resizeTimeout = setTimeout(() => renderingEngine.current?.resize(), 100); - }); - resizeObserver.observe(container); - return () => { - if (resizeTimeout) clearTimeout(resizeTimeout); - resizeObserver.unobserve(container); - resizeObserver.disconnect(); - }; - }, []); - - return ( -
-
-
-
-
- ); -}; diff --git a/apps/desktop/src/pages/Viewer/MprViewer/Crosshair.config.tsx b/apps/desktop/src/pages/Viewer/MprViewer/index.config.tsx similarity index 63% rename from apps/desktop/src/pages/Viewer/MprViewer/Crosshair.config.tsx rename to apps/desktop/src/pages/Viewer/MprViewer/index.config.tsx index 7d8e9cf..a0184df 100644 --- a/apps/desktop/src/pages/Viewer/MprViewer/Crosshair.config.tsx +++ b/apps/desktop/src/pages/Viewer/MprViewer/index.config.tsx @@ -34,3 +34,23 @@ export const viewportReferenceLineSlabThicknessControlsOn = [ export const volumeName = "CT_VOLUME_ID"; // Id of the volume less loader prefix export const volumeLoaderScheme = "cornerstoneStreamingImageVolume"; // Loader id which defines which volume loader to use export const volumeId = `${volumeLoaderScheme}:${volumeName}`; // VolumeId with loader id + volume id +export const toolGroupMprId = "toolMprNo1"; + +export function getReferenceLineColor(vpId: ViewportId) { + return viewportColors[vpId]; +} + +export function getReferenceLineControllable(vpId: ViewportId) { + const index = viewportReferenceLineControllable.indexOf(vpId); + return index !== -1; +} + +export function getReferenceLineDraggableRotatable(vpId: ViewportId) { + const index = viewportReferenceLineDraggableRotatable.indexOf(vpId); + return index !== -1; +} + +export function getReferenceLineSlabThicknessControlsOn(vpId: ViewportId) { + const index = viewportReferenceLineSlabThicknessControlsOn.indexOf(vpId); + return index !== -1; +} diff --git a/apps/desktop/src/pages/Viewer/StackViewer/index.config.ts b/apps/desktop/src/pages/Viewer/StackViewer/index.config.ts new file mode 100644 index 0000000..172fda7 --- /dev/null +++ b/apps/desktop/src/pages/Viewer/StackViewer/index.config.ts @@ -0,0 +1,2 @@ +export const stackViewportId = "stackNo1"; +export const toolGroupStackId = "toolStackNo1"; diff --git a/apps/desktop/src/pages/Viewer/StackViewer/index.tsx b/apps/desktop/src/pages/Viewer/StackViewer/index.tsx deleted file mode 100644 index 1b41b5d..0000000 --- a/apps/desktop/src/pages/Viewer/StackViewer/index.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { useEffect, useRef, useState } from "react"; -import { PublicViewportInput } from "@cornerstonejs/core/dist/types/types/IViewport.js"; -import { Enums as CoreEnums, RenderingEngine } from "@cornerstonejs/core"; -import { IStackViewport } from "@cornerstonejs/core/dist/types/types"; -import { Slider } from "@/components/ui/slider"; - -export interface StackViewerProps { - imageIds: string[]; -} - -export const StackViewer = (props: StackViewerProps) => { - const viewportStackRef = useRef(null); - const renderingEngineRef = useRef(); - const containerRef = useRef(null); - const viewportId = "stackViewport"; - const renderingEngineId = "stackRenderingEngine"; - - useEffect(() => { - if (!viewportStackRef.current) return; - renderingEngineRef.current = new RenderingEngine(renderingEngineId); - const viewportInput: PublicViewportInput = { - viewportId, - type: CoreEnums.ViewportType.STACK, // 用于 Stack 视图 - element: viewportStackRef.current, - defaultOptions: { - background: [0, 0, 0], - }, - }; - renderingEngineRef.current.enableElement(viewportInput); - const viewport = renderingEngineRef.current.getViewport( - viewportId - ) as IStackViewport; - - viewport.setStack(props.imageIds); - - return () => { - renderingEngineRef.current?.disableElement(viewportId); - renderingEngineRef.current?.destroy(); - }; - }, [props.imageIds, renderingEngineId, viewportId]); - - useEffect(() => { - const container = containerRef.current; - if (!container) return; - let resizeTimeout: NodeJS.Timeout | null = null; - const resizeObserver = new ResizeObserver(() => { - if (resizeTimeout) clearTimeout(resizeTimeout); - resizeTimeout = setTimeout( - () => renderingEngineRef.current?.resize(), - 100 - ); - }); - resizeObserver.observe(container); - return () => { - if (resizeTimeout) clearTimeout(resizeTimeout); - resizeObserver.unobserve(container); - resizeObserver.disconnect(); - }; - }, []); - - const onChangeIndex = (value: number[]) => { - if (renderingEngineRef.current) { - const viewport = renderingEngineRef.current.getViewport(viewportId); - (viewport as IStackViewport).setImageIdIndex(value[0]); - } - }; - - return ( -
-
- -
- ); -}; diff --git a/apps/desktop/src/pages/Viewer/MprViewer/ToolBarMenu/index.tsx b/apps/desktop/src/pages/Viewer/ToolBarMenu/index.tsx similarity index 100% rename from apps/desktop/src/pages/Viewer/MprViewer/ToolBarMenu/index.tsx rename to apps/desktop/src/pages/Viewer/ToolBarMenu/index.tsx diff --git a/apps/desktop/src/pages/Viewer/index.tsx b/apps/desktop/src/pages/Viewer/index.tsx index 3a81196..a1ddb69 100644 --- a/apps/desktop/src/pages/Viewer/index.tsx +++ b/apps/desktop/src/pages/Viewer/index.tsx @@ -1,31 +1,87 @@ import { useLocation } from "react-router-dom"; import { initCornerstone } from "./MprViewer/CornerstoneDicomLoader/init"; -import { CrosshairMpr } from "./MprViewer/Crosshair"; +import * as cornerstoneTools from "@cornerstonejs/tools"; import { useEffect, useRef, useState } from "react"; import { ResizableHandle, ResizablePanel, ResizablePanelGroup, } from "@/components/ui/resizable"; -import { StackViewer } from "./StackViewer"; import { createImageIdsAndCacheMetaData } from "./MprViewer/CornerstoneDicomLoader/createImageIdsAndCacheMetaData"; -import { volumeLoader } from "@cornerstonejs/core"; +import { + RenderingEngine, + setVolumesForViewports, + volumeLoader, + Enums as CoreEnums, +} from "@cornerstonejs/core"; +import { + IStackViewport, + PublicViewportInput, +} from "@cornerstonejs/core/dist/types/types"; +import { + getReferenceLineColor, + getReferenceLineControllable, + getReferenceLineDraggableRotatable, + getReferenceLineSlabThicknessControlsOn, + toolGroupMprId, + viewportId1, + viewportId2, + viewportId3, + volumeId, +} from "./MprViewer/index.config"; +import setCtTransferFunctionForVolumeActor from "./MprViewer/CornerstoneDicomLoader/setCtTransferFunctionForVolumeActor"; +import { Slider } from "@/components/ui/slider"; +import { stackViewportId, toolGroupStackId } from "./StackViewer/index.config"; +import { ToolBarMenu } from "./ToolBarMenu"; +import { Model3DViewer } from "./ModelViewer"; + +const { + ToolGroupManager, + CrosshairsTool, + StackScrollMouseWheelTool, + WindowLevelTool, + ZoomTool, + Enums: csToolsEnums, +} = cornerstoneTools; + +const { MouseBindings } = csToolsEnums; const wadoRsRoot = "http://localhost:8042/dicom-web"; +const { ViewportType, OrientationAxis } = CoreEnums; +const wwwl = { windowCenter: 50, windowWidth: 850 }; + export const Viewer = () => { - const [cornerstoneLoaded, setCornerstoneLoaded] = useState(false); + const volumeViewport1Ref = useRef(null); + const volumeViewport2Ref = useRef(null); + const volumeViewport3Ref = useRef(null); + const stackViewportRef = useRef(null); + + /** + * 当前的index + */ + const [index, setIndex] = useState(0); const location = useLocation(); const queryParams = new URLSearchParams(location.search); const SeriesInstanceUID = queryParams.get("SeriesInstanceUID"); const StudyInstanceUID = queryParams.get("StudyInstanceUID"); - const imageIdsRef = useRef(); - const volumeId = "volume"; - const wwwl = { windowCenter: 50, windowWidth: 850 }; + const [imageIds, setImageIds] = useState(); + const renderingEngineId = "renderEngineNo1"; + const renderingEngineRef = useRef(); useEffect(() => { - const setImageOrderCache = async () => { + cornerstoneTools.addTool(StackScrollMouseWheelTool); + cornerstoneTools.addTool(CrosshairsTool); + cornerstoneTools.addTool(WindowLevelTool); + cornerstoneTools.addTool(ZoomTool); + + const renderViewport = async () => { if (!StudyInstanceUID || !SeriesInstanceUID) return; + if (!volumeViewport1Ref.current) return; + if (!volumeViewport2Ref.current) return; + if (!volumeViewport3Ref.current) return; + if (!stackViewportRef.current) return; + renderingEngineRef.current = new RenderingEngine(renderingEngineId); // imageIds此时由于流式加载for mpr,是错乱的图片顺序 const imageIds = await createImageIdsAndCacheMetaData({ StudyInstanceUID, @@ -37,26 +93,198 @@ export const Viewer = () => { imageIds, }); volume.load(); - imageIdsRef.current = volume.imageIds; + + const volumeViewportInput: PublicViewportInput[] = [ + { + viewportId: viewportId1, + type: ViewportType.ORTHOGRAPHIC, + element: volumeViewport1Ref.current, + defaultOptions: { + orientation: OrientationAxis.AXIAL, + background: [0, 0, 0], + }, + }, + { + viewportId: viewportId2, + type: ViewportType.ORTHOGRAPHIC, + element: volumeViewport2Ref.current, + defaultOptions: { + orientation: OrientationAxis.SAGITTAL, + background: [0, 0, 0], + }, + }, + { + viewportId: viewportId3, + type: ViewportType.ORTHOGRAPHIC, + element: volumeViewport3Ref.current, + defaultOptions: { + orientation: OrientationAxis.CORONAL, + background: [0, 0, 0], + }, + }, + ]; + const stackViewportInput: PublicViewportInput = { + viewportId: stackViewportId, + type: ViewportType.STACK, // 用于 Stack 视图 + element: stackViewportRef.current, + defaultOptions: { + background: [0, 0, 0], + }, + }; + + renderingEngineRef.current.setViewports([ + ...volumeViewportInput, + stackViewportInput, + ]); + renderingEngineRef.current.enableElement(stackViewportInput); + const stackViewport = renderingEngineRef.current.getViewport( + stackViewportId + ) as IStackViewport; + + stackViewport.setStack(volume.imageIds); + setImageIds(volume.imageIds); + + await setVolumesForViewports( + renderingEngineRef.current, + [ + { + volumeId: volumeId, + callback: ({ volumeActor }) => + setCtTransferFunctionForVolumeActor({ + volumeActor, + defaultWindowCenter: wwwl.windowCenter, + defaultWindowWidth: wwwl.windowWidth, + }), + }, + ], + [viewportId1, viewportId2, viewportId3] + ); + + const toolGroupMpr = ToolGroupManager.createToolGroup(toolGroupMprId); + if (toolGroupMpr) { + toolGroupMpr.addViewport(viewportId1, renderingEngineId); + toolGroupMpr.addViewport(viewportId2, renderingEngineId); + toolGroupMpr.addViewport(viewportId3, renderingEngineId); + toolGroupMpr.addTool(ZoomTool.toolName); + toolGroupMpr.setToolActive(ZoomTool.toolName, { + bindings: [{ mouseButton: MouseBindings.Secondary }], + }); + toolGroupMpr.addTool(StackScrollMouseWheelTool.toolName); + toolGroupMpr.addTool(CrosshairsTool.toolName, { + getReferenceLineColor, + getReferenceLineControllable, + getReferenceLineDraggableRotatable, + getReferenceLineSlabThicknessControlsOn, + }); + toolGroupMpr.setToolActive(CrosshairsTool.toolName, { + bindings: [{ mouseButton: MouseBindings.Primary }], + }); + toolGroupMpr.setToolActive(StackScrollMouseWheelTool.toolName); + + toolGroupMpr.addTool(WindowLevelTool.toolName); + toolGroupMpr.setToolActive(WindowLevelTool.toolName, { + bindings: [{ mouseButton: MouseBindings.Auxiliary }], + }); + } + + const toolGroupStack = ToolGroupManager.createToolGroup(toolGroupStackId); + if (toolGroupStack) { + toolGroupStack.addViewport(stackViewportId, renderingEngineId); + toolGroupStack.addTool(ZoomTool.toolName); + toolGroupStack.setToolActive(ZoomTool.toolName, { + bindings: [{ mouseButton: MouseBindings.Secondary }], + }); + toolGroupStack.addTool(WindowLevelTool.toolName); + toolGroupStack.setToolActive(WindowLevelTool.toolName, { + bindings: [{ mouseButton: MouseBindings.Auxiliary }], + }); + } // 默认windowWidtth const { windowCenter, windowWidth } = volume.cornerstoneImageMetaData; console.log("默认窗宽/位: ", windowCenter, windowWidth); - - setCornerstoneLoaded(true); }; initCornerstone(() => { - setImageOrderCache(); + renderViewport(); }); + return () => { + renderingEngineRef.current?.disableElement(stackViewportId); + renderingEngineRef.current?.disableElement(viewportId1); + renderingEngineRef.current?.disableElement(viewportId2); + renderingEngineRef.current?.disableElement(viewportId3); + renderingEngineRef.current?.destroy(); + + ToolGroupManager.destroyToolGroup(toolGroupMprId); + ToolGroupManager.destroyToolGroup(toolGroupStackId); + + cornerstoneTools.removeTool(StackScrollMouseWheelTool); + cornerstoneTools.removeTool(CrosshairsTool); + cornerstoneTools.removeTool(WindowLevelTool); + cornerstoneTools.removeTool(ZoomTool); + }; }, [SeriesInstanceUID, StudyInstanceUID]); + const onChangeIndex = (value: number[]) => { + setIndex(value[0]); + }; + + useEffect( + () => setIndex(imageIds ? Math.floor(imageIds.length / 2) : 0), + [imageIds] + ); + + /** + * 滚轮换图逻辑 + */ + useEffect(() => { + const handleWheel = (event: WheelEvent) => { + console.log("wheel"); + if (!imageIds) return; + const delta = event.deltaY > 0 ? -1 : 1; + if (delta === -1 && index === 0) return; + if (delta === 1 && index === imageIds?.length - 1) return; + setIndex((p) => p + delta); + }; + const stackElement = stackViewportRef.current; + stackElement?.addEventListener("wheel", handleWheel); + return () => { + stackElement?.removeEventListener("wheel", handleWheel); + }; + }, [imageIds, index]); + + useEffect(() => { + if (renderingEngineRef.current) { + const viewport = renderingEngineRef.current.getViewport(stackViewportId); + (viewport as IStackViewport)?.setImageIdIndex(index); + } + }, [index]); + + useEffect(() => { + const container = stackViewportRef.current; + if (!container) return; + let resizeTimeout: NodeJS.Timeout | null = null; + const resizeObserver = new ResizeObserver(() => { + if (resizeTimeout) clearTimeout(resizeTimeout); + resizeTimeout = setTimeout( + () => renderingEngineRef.current?.resize(), + 100 + ); + }); + resizeObserver.observe(container); + return () => { + if (resizeTimeout) clearTimeout(resizeTimeout); + resizeObserver.unobserve(container); + resizeObserver.disconnect(); + }; + }, []); + return (
- {/*
+
-
*/} +
@@ -66,24 +294,38 @@ export const Viewer = () => { className="w-full h-full" > -
- {cornerstoneLoaded && imageIdsRef.current && ( - +
+
+ {imageIds && ( +
+
+ {index + 1}/{imageIds.length} +
+ +
)}
-
bototm
+
+ +
- {cornerstoneLoaded && imageIdsRef.current && ( - - )} +
+
+