diff --git a/dicom-viewer/src/App.tsx b/dicom-viewer/src/App.tsx index 7c8179a..d25a938 100644 --- a/dicom-viewer/src/App.tsx +++ b/dicom-viewer/src/App.tsx @@ -407,12 +407,14 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer total: number, windowCenter: string, windowWidth: string, + slicePosition?: number, }>>(() => Array(viewportGrid.rows * viewportGrid.cols).fill({ index: 0, total: 0, windowCenter: "", windowWidth: "", + slicePosition: 0, }) ); @@ -741,70 +743,76 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer // Update overlay info for a specific viewport -const updateOverlay = useCallback((viewportIdx: number, viewport: any) => { - console.log("updateOverlay called for viewportIdx:", viewportIdx, "viewport:", viewport); - if (!viewport) return; + const updateOverlay = useCallback((viewportIdx: number, viewport: any) => { + if (!viewport) return; - let currentIndex = 0; - let total = 0; - let windowCenter = ""; - let windowWidth = ""; - console.log("Updating overlay for viewport:", viewportIdx, viewportModes[viewportIdx]); + let currentIndex = 0; + let total = 0; + let windowCenter = ""; + let windowWidth = ""; + let slicePosition = ""; - if (viewportModes[viewportIdx] === "stack") { - currentIndex = typeof viewport.getCurrentImageIdIndex === "function" - ? viewport.getCurrentImageIdIndex() - : 0; - const imageIds = typeof viewport.getImageIds === "function" - ? viewport.getImageIds() - : []; - total = imageIds.length; - if (typeof viewport.getProperties === "function") { - const props = viewport.getProperties(); - if (props.voiRange) { - const { lower, upper } = props.voiRange; - windowCenter = ((upper + lower) / 2).toFixed(0); - windowWidth = (upper - lower).toFixed(0); + if (viewportModes[viewportIdx] === "stack") { + currentIndex = typeof viewport.getCurrentImageIdIndex === "function" + ? viewport.getCurrentImageIdIndex() + : 0; + const imageIds = typeof viewport.getImageIds === "function" + ? viewport.getImageIds() + : []; + total = imageIds.length; + if (typeof viewport.getProperties === "function") { + const props = viewport.getProperties(); + if (props.voiRange) { + const { lower, upper } = props.voiRange; + windowCenter = ((upper + lower) / 2).toFixed(0); + windowWidth = (upper - lower).toFixed(0); + } + } + } else if (viewportModes[viewportIdx] === "volume") { + // For volume, get slice index and number of slices if available + if (typeof viewport.getSliceIndex === "function") { + currentIndex = viewport.getSliceIndex(); + } + if (typeof viewport.getNumberOfSlices === "function") { + total = viewport.getNumberOfSlices(); + } + // Get VOI from getProperties (may need to pass volumeId if multiple volumes) + if (typeof viewport.getProperties === "function") { + let props; + if (viewport.volumeIds && viewport.volumeIds.size > 0) { + // If multiple volumes, get the first one + const volumeId = Array.from(viewport.volumeIds)[0]; + props = viewport.getProperties(volumeId); + } else { + props = viewport.getProperties(); + } + if (props && props.voiRange) { + const { lower, upper } = props.voiRange; + windowCenter = ((upper + lower) / 2).toFixed(0); + windowWidth = (upper - lower).toFixed(0); + } + } + // Try to get slice position in mm (distance along viewPlaneNormal from origin) + if (typeof viewport.getSlicePlaneCoordinates === "function") { + const coords = viewport.getSlicePlaneCoordinates(); + if (coords && typeof coords.position === "number") { + slicePosition = coords.position.toFixed(2) + " mm"; + } } } - } else if (viewportModes[viewportIdx] === "volume") { - console.log(viewport) - // For volume, get slice index and number of slices if available - if (typeof viewport.getSliceIndex === "function") { - currentIndex = viewport.getSliceIndex(); - } - if (typeof viewport.getNumberOfSlices === "function") { - total = viewport.getNumberOfSlices(); - } - // Get VOI from getProperties (may need to pass volumeId if multiple volumes) - if (typeof viewport.getProperties === "function") { - let props; - if (viewport.volumeIds && viewport.volumeIds.size > 0) { - // If multiple volumes, get the first one - const volumeId = Array.from(viewport.volumeIds)[0]; - props = viewport.getProperties(volumeId); - } else { - props = viewport.getProperties(); - } - if (props && props.voiRange) { - const { lower, upper } = props.voiRange; - windowCenter = ((upper + lower) / 2).toFixed(0); - windowWidth = (upper - lower).toFixed(0); - } - } - } - setImageInfos(prev => { - const next = [...prev]; - next[viewportIdx] = { - index: currentIndex + 1, - total, - windowCenter, - windowWidth, - }; - return next; - }); -}, [viewportModes]); + setImageInfos(prev => { + const next = [...prev]; + next[viewportIdx] = { + index: currentIndex + 1, + total, + windowCenter, + windowWidth, + slicePosition, // add this field + }; + return next; + }); + }, [viewportModes]); const [gridBtnActive, setGridBtnActive] = useState(false); @@ -918,7 +926,6 @@ const updateOverlay = useCallback((viewportIdx: number, viewport: any) => { } if (viewportModes[i] === 'stack') { - //if (imageIds && !restoringStateRef.current) { if (imageIds && imageIds.length > 0) { viewport.setStack(imageIds); } @@ -950,6 +957,16 @@ const updateOverlay = useCallback((viewportIdx: number, viewport: any) => { volume.load(); viewport.setVolumes([{ volumeId }]); viewport.render(); + + // Always (re-)attach overlay update for each viewport + if ((element)._overlayListener) { + // Remove previous listener if present + viewport.element.removeEventListener(IMAGE_RENDERED, (element)._overlayListener); + } + const overlayListener = () => updateOverlay(i, viewport); + viewport.element.addEventListener(IMAGE_RENDERED, overlayListener); + (element)._overlayListener = overlayListener; + updateOverlay(i, viewport); continue } viewport.render(); @@ -991,7 +1008,6 @@ const updateOverlay = useCallback((viewportIdx: number, viewport: any) => { } - // Always (re-)attach overlay update for each viewport if ((element)._overlayListener) { // Remove previous listener if present @@ -1001,8 +1017,14 @@ const updateOverlay = useCallback((viewportIdx: number, viewport: any) => { viewport.element.addEventListener(IMAGE_RENDERED, overlayListener); (element)._overlayListener = overlayListener; updateOverlay(i, viewport); + } - + + + + + + const activeVolumeViewports = []; for (let i = 0; i < viewportGrid.rows * viewportGrid.cols; i++) { const viewportId = `CT_${i}`; @@ -1369,17 +1391,17 @@ const updateOverlay = useCallback((viewportIdx: number, viewport: any) => { } // Attach to window with a unique name - window[`getViewport_${apiKey}`] = (idx: number) => { - const renderingEngine = renderingEngineRef.current; - if (!renderingEngine) return undefined; - return renderingEngine.getViewport(`CT_${idx}`); - }; - window[`getAllViewports_${apiKey}`] = () => { - const renderingEngine = renderingEngineRef.current; - if (!renderingEngine) return []; - // Return all currently active viewports as an array - return Array.from({ length: MAX_VIEWPORTS }, (_, i) => renderingEngine.getViewport(`CT_${i}`)).filter(Boolean); - }; + window[`getViewport_${apiKey}`] = (idx: number) => { + const renderingEngine = renderingEngineRef.current; + if (!renderingEngine) return undefined; + return renderingEngine.getViewport(`CT_${idx}`); + }; + window[`getAllViewports_${apiKey}`] = () => { + const renderingEngine = renderingEngineRef.current; + if (!renderingEngine) return []; + // Return all currently active viewports as an array + return Array.from({ length: MAX_VIEWPORTS }, (_, i) => renderingEngine.getViewport(`CT_${i}`)).filter(Boolean); + }; window[`jumpToSliceByImageId_${apiKey}`] = jumpToSliceByImageId; window[`resetViewer_${apiKey}`] = resetViewer; @@ -1454,9 +1476,9 @@ const updateOverlay = useCallback((viewportIdx: number, viewport: any) => { console.log("viewport", viewportId, "properties", p); // --- Volume orientation and slice index --- if (modes[i] === "volume") { - console.log("Getting orientation for viewport", i); - console.log("viewport.viewportProperties", viewport.viewportProperties); - o = viewport.viewportProperties?.orientation || null; + console.log("Getting orientation for viewport", i); + console.log("viewport.viewportProperties", viewport.viewportProperties); + o = viewport.viewportProperties?.orientation || null; if (typeof viewport.getSliceIndex === "function") { vIdx = viewport.getSliceIndex(); } @@ -1528,19 +1550,19 @@ const updateOverlay = useCallback((viewportIdx: number, viewport: any) => { console.log("viewport.getImageIds", viewport.getImageIds?.()); if (!importedImageIds.length) continue; try { - const viewportImageIds = viewport.getImageIds?.() || []; - if ( - viewportImageIds.length !== importedImageIds.length || - !viewportImageIds.every((id, idx) => id === importedImageIds[idx]) - ) { - console.warn( - `Viewport ${i}: importedImageIds do not match viewportImageIds`, - { importedImageIds, viewportImageIds } - ); - allReady = false; - break; - } - } catch(e) { + const viewportImageIds = viewport.getImageIds?.() || []; + if ( + viewportImageIds.length !== importedImageIds.length || + !viewportImageIds.every((id, idx) => id === importedImageIds[idx]) + ) { + console.warn( + `Viewport ${i}: importedImageIds do not match viewportImageIds`, + { importedImageIds, viewportImageIds } + ); + allReady = false; + break; + } + } catch (e) { allReady = false; } @@ -2371,8 +2393,12 @@ const updateOverlay = useCallback((viewportIdx: number, viewport: any) => { ) : ( - // Volume mode: do not show image position
+ Slice: {imageInfos[i]?.index} / {imageInfos[i]?.total} + {imageInfos[i]?.slicePosition && ( +   ({imageInfos[i]?.slicePosition}) + )} +
WC: {imageInfos[i]?.windowCenter}   WW: {imageInfos[i]?.windowWidth}
)}