diff --git a/README.md b/README.md index aa27683..ea5886b 100644 --- a/README.md +++ b/README.md @@ -38,4 +38,20 @@ pnpm config set virtual-store-dir-max-length 70 - 心脏结构窗 窗宽:500 HU 窗位:50 HU -适用于查看整体心脏结构,包括心室腔、心包和大血管。 \ No newline at end of file +适用于查看整体心脏结构,包括心室腔、心包和大血管。 + + +## 备忘 + +- AXIAL(轴位): + • 方向:横断面,与身体的长轴垂直。 + • 描述:将身体分为上(头部)和下(脚部)部分。 + • 应用:通常用于查看从头顶到脚底的切片图像。 +- SAGITTAL(矢状位): + • 方向:纵向面,沿身体的前后方向。 + • 描述:将身体分为左、右两部分。正中矢状面则正好将身体分为左右对称的两半。 + • 应用:用于观察身体的侧面结构,如脊柱的排列。 +- CORONAL(冠状位): + • 方向:冠状面,与矢状面垂直,沿身体的左右方向。 + • 描述:将身体分为前(腹侧)和后(背侧)部分。 + • 应用:适用于查看从前面到后面的结构,如面部和脑部的解剖。 \ No newline at end of file diff --git a/apps/desktop/electron/core/pacs.ts b/apps/desktop/electron/core/pacs.ts index dff16cd..61e48a3 100644 --- a/apps/desktop/electron/core/pacs.ts +++ b/apps/desktop/electron/core/pacs.ts @@ -27,8 +27,8 @@ export const runOrthancServer = (pacsPath: string) => { if (existsSync(pacsPath)) { const configPath = path.join(path.dirname(pacsPath), "config.json"); const child_process = spawn(pacsPath, [configPath]); - child_process.stdout.on("data", (data) => log.info(data)); - child_process.stderr.on("data", (data) => log.error(data)); + child_process.stdout.on("data", (data) => log.info(data.toString())); + child_process.stderr.on("data", (data) => log.error(data.toString())); } else { console.error("pacsPath is a not exist"); log.error("pacsPath is a not exist"); diff --git a/apps/desktop/src/pages/Viewer/MprViewer/Crosshair.config.tsx b/apps/desktop/src/pages/Viewer/MprViewer/Crosshair.config.tsx index d109039..7d8e9cf 100644 --- a/apps/desktop/src/pages/Viewer/MprViewer/Crosshair.config.tsx +++ b/apps/desktop/src/pages/Viewer/MprViewer/Crosshair.config.tsx @@ -1,9 +1,10 @@ export const renderingEngineId = "mprRenderingEngine"; -export const viewportId1 = "CT_AXIAL"; -export const viewportId2 = "CT_SAGITTAL"; -export const viewportId3 = "CT_CORONAL"; -export const toolGroupId = "group"; +export type ViewportId = "CT_AXIAL" | "CT_SAGITTAL" | "CT_CORONAL"; + +export const viewportId1: ViewportId = "CT_AXIAL"; +export const viewportId2: ViewportId = "CT_SAGITTAL"; +export const viewportId3: ViewportId = "CT_CORONAL"; export const viewportColors = { [viewportId1]: "rgb(200, 0, 0)", @@ -11,7 +12,7 @@ export const viewportColors = { [viewportId3]: "rgb(0, 200, 0)", }; -export const viewportReferenceLineControllable = [ +export const viewportReferenceLineControllable: ViewportId[] = [ viewportId1, viewportId2, viewportId3, diff --git a/apps/desktop/src/pages/Viewer/MprViewer/Crosshair.tsx b/apps/desktop/src/pages/Viewer/MprViewer/Crosshair.tsx index b2be293..7ff1707 100644 --- a/apps/desktop/src/pages/Viewer/MprViewer/Crosshair.tsx +++ b/apps/desktop/src/pages/Viewer/MprViewer/Crosshair.tsx @@ -17,10 +17,16 @@ import { viewportId1, viewportId2, viewportId3, + ViewportId, } from "./Crosshair.config"; -const { ToolGroupManager, CrosshairsTool, StackScrollMouseWheelTool, WindowLevelTool, Enums: csToolsEnums } = - cornerstoneTools; +const { + ToolGroupManager, + CrosshairsTool, + StackScrollMouseWheelTool, + WindowLevelTool, + Enums: csToolsEnums, +} = cornerstoneTools; const { MouseBindings } = csToolsEnums; @@ -30,23 +36,22 @@ interface CrosshairMprProps { children?: JSX.Element; } -function getReferenceLineColor(viewportId: string | number) { - return viewportColors[viewportId]; +function getReferenceLineColor(vpId: ViewportId) { + return viewportColors[vpId]; } -function getReferenceLineControllable(viewportId: string) { - const index = viewportReferenceLineControllable.indexOf(viewportId); +function getReferenceLineControllable(vpId: ViewportId) { + const index = viewportReferenceLineControllable.indexOf(vpId); return index !== -1; } -function getReferenceLineDraggableRotatable(viewportId: string) { - const index = viewportReferenceLineDraggableRotatable.indexOf(viewportId); +function getReferenceLineDraggableRotatable(vpId: ViewportId) { + const index = viewportReferenceLineDraggableRotatable.indexOf(vpId); return index !== -1; } -function getReferenceLineSlabThicknessControlsOn(viewportId: string) { - const index = - viewportReferenceLineSlabThicknessControlsOn.indexOf(viewportId); +function getReferenceLineSlabThicknessControlsOn(vpId: ViewportId) { + const index = viewportReferenceLineSlabThicknessControlsOn.indexOf(vpId); return index !== -1; } @@ -56,19 +61,25 @@ interface CrosshairMprProps { } export const CrosshairMpr = (props: CrosshairMprProps) => { - const viewportId1Ref = useRef(null); - const viewportId2Ref = useRef(null); - const viewportId3Ref = useRef(null); + const viewportRef_AXIAL = useRef(null); + const viewportRef_SAGITTAL = useRef(null); + const viewportRef_CORONAL = useRef(null); const renderingEngine = useRef(); const imageIds = useRef(); useEffect(() => { - cornerstoneTools.addTool(StackScrollMouseWheelTool); cornerstoneTools.addTool(CrosshairsTool); cornerstoneTools.addTool(WindowLevelTool); const run = async () => { + if ( + !viewportRef_AXIAL.current || + !viewportRef_SAGITTAL.current || + !viewportRef_CORONAL.current + ) + return; + imageIds.current = await createImageIdsAndCacheMetaData({ StudyInstanceUID: props.StudyInstanceUID, SeriesInstanceUID: props.SeriesInstanceUID, @@ -86,17 +97,17 @@ export const CrosshairMpr = (props: CrosshairMprProps) => { ); // 默认windowWidtth - const { windowCenter, windowWidth } = volume.cornerstoneImageMetaData - const defaultWindowCenter = 50 - const defaultWindowWidth = 850 - console.log(2, windowCenter, windowWidth) + const { windowCenter, windowWidth } = volume.cornerstoneImageMetaData; + const defaultWindowCenter = 50; + const defaultWindowWidth = 850; + console.log(2, windowCenter, windowWidth); // Create the viewports const viewportInputArray: PublicViewportInput[] = [ { viewportId: viewportId1, type: ViewportType.ORTHOGRAPHIC, - element: viewportId1Ref.current!, + element: viewportRef_AXIAL.current, defaultOptions: { orientation: CoreEnums.OrientationAxis.AXIAL, background: [0, 0, 0], @@ -105,7 +116,7 @@ export const CrosshairMpr = (props: CrosshairMprProps) => { { viewportId: viewportId2, type: ViewportType.ORTHOGRAPHIC, - element: viewportId2Ref.current!, + element: viewportRef_SAGITTAL.current, defaultOptions: { orientation: CoreEnums.OrientationAxis.SAGITTAL, background: [0, 0, 0], @@ -114,7 +125,7 @@ export const CrosshairMpr = (props: CrosshairMprProps) => { { viewportId: viewportId3, type: ViewportType.ORTHOGRAPHIC, - element: viewportId3Ref.current!, + element: viewportRef_CORONAL.current, defaultOptions: { orientation: CoreEnums.OrientationAxis.CORONAL, background: [0, 0, 0], @@ -133,7 +144,12 @@ export const CrosshairMpr = (props: CrosshairMprProps) => { [ { volumeId: props.SeriesInstanceUID, - callback: ({ volumeActor }) => setCtTransferFunctionForVolumeActor({ volumeActor, defaultWindowCenter, defaultWindowWidth }), + callback: ({ volumeActor }) => + setCtTransferFunctionForVolumeActor({ + volumeActor, + defaultWindowCenter, + defaultWindowWidth, + }), }, ], [viewportId1, viewportId2, viewportId3] @@ -187,17 +203,18 @@ export const CrosshairMpr = (props: CrosshairMprProps) => { ]); }; - if (!props.SeriesInstanceUID && !props.StudyInstanceUID) return; - run(); - + if (props.SeriesInstanceUID && props.StudyInstanceUID) { + run(); + } return () => { cornerstoneTools.removeTool(StackScrollMouseWheelTool); cornerstoneTools.removeTool(CrosshairsTool); - cornerstoneTools.removeTool(WindowLevelTool) + cornerstoneTools.removeTool(WindowLevelTool); + renderingEngine.current?.destroy(); ToolGroupManager.destroyToolGroup(props.SeriesInstanceUID); }; - }, [props.StudyInstanceUID, props.SeriesInstanceUID]); + }, [props]); useEffect(() => { const resize = () => renderingEngine.current?.resize(); @@ -207,11 +224,19 @@ export const CrosshairMpr = (props: CrosshairMprProps) => { }; }, []); + const onDBClickViewport = (viewportRef) => { + console.log("dblcik", viewportRef); + }; + return (
-
-
-
+
onDBClickViewport(viewportRef_AXIAL)} + className="h-1/3 border rounded-md" + ref={viewportRef_AXIAL} + >
+
+
); }; diff --git a/apps/desktop/src/pages/Viewer/index.tsx b/apps/desktop/src/pages/Viewer/index.tsx index 6c75c70..63697c9 100644 --- a/apps/desktop/src/pages/Viewer/index.tsx +++ b/apps/desktop/src/pages/Viewer/index.tsx @@ -3,15 +3,27 @@ import { initCornerstone } from "./MprViewer/CornerstoneDicomLoader/init"; import { CrosshairMpr } from "./MprViewer/Crosshair"; import { useEffect, useState } from "react"; +export interface CurrentDicom { + SeriesInstanceUID: string | null; + StudyInstanceUID: string | null; +} + export const Viewer = () => { const [cornerstoneLoaded, setCornerstoneLoaded] = useState(false); const location = useLocation(); const queryParams = new URLSearchParams(location.search); - const SeriesInstanceUID = queryParams.get("SeriesInstanceUID"); // 获取URL参数 - const StudyInstanceUID = queryParams.get("StudyInstanceUID"); // 获取URL参数 + const [currentDicom, setCurrentDicom] = useState({ + SeriesInstanceUID: queryParams.get("SeriesInstanceUID"), + StudyInstanceUID: queryParams.get("StudyInstanceUID"), + }); - console.log(StudyInstanceUID); - console.log(SeriesInstanceUID); + useEffect(() => { + const { SeriesInstanceUID, StudyInstanceUID } = currentDicom; + if (StudyInstanceUID && SeriesInstanceUID) { + const metadata = { SeriesInstanceUID, StudyInstanceUID }; + localStorage.setItem("viewCache", JSON.stringify(metadata)); + } + }, [currentDicom]); useEffect(() => { initCornerstone().then(() => setCornerstoneLoaded(true)); @@ -21,12 +33,15 @@ export const Viewer = () => {
vr
- {cornerstoneLoaded && StudyInstanceUID && SeriesInstanceUID && ( - - )} + {cornerstoneLoaded && + currentDicom.StudyInstanceUID && + currentDicom.SeriesInstanceUID && ( + + )} + {Object.values(currentDicom).some((i) => !i) && "导入dicom"}
);