fix viewport overlay

This commit is contained in:
Ross
2025-06-23 11:48:46 +01:00
parent 98c71e73e2
commit 28884670e4
+116 -90
View File
@@ -407,12 +407,14 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer
total: number, total: number,
windowCenter: string, windowCenter: string,
windowWidth: string, windowWidth: string,
slicePosition?: number,
}>>(() => }>>(() =>
Array(viewportGrid.rows * viewportGrid.cols).fill({ Array(viewportGrid.rows * viewportGrid.cols).fill({
index: 0, index: 0,
total: 0, total: 0,
windowCenter: "", windowCenter: "",
windowWidth: "", windowWidth: "",
slicePosition: 0,
}) })
); );
@@ -741,70 +743,76 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer
// Update overlay info for a specific viewport // Update overlay info for a specific viewport
const updateOverlay = useCallback((viewportIdx: number, viewport: any) => { const updateOverlay = useCallback((viewportIdx: number, viewport: any) => {
console.log("updateOverlay called for viewportIdx:", viewportIdx, "viewport:", viewport); if (!viewport) return;
if (!viewport) return;
let currentIndex = 0; let currentIndex = 0;
let total = 0; let total = 0;
let windowCenter = ""; let windowCenter = "";
let windowWidth = ""; let windowWidth = "";
console.log("Updating overlay for viewport:", viewportIdx, viewportModes[viewportIdx]); let slicePosition = "";
if (viewportModes[viewportIdx] === "stack") { if (viewportModes[viewportIdx] === "stack") {
currentIndex = typeof viewport.getCurrentImageIdIndex === "function" currentIndex = typeof viewport.getCurrentImageIdIndex === "function"
? viewport.getCurrentImageIdIndex() ? viewport.getCurrentImageIdIndex()
: 0; : 0;
const imageIds = typeof viewport.getImageIds === "function" const imageIds = typeof viewport.getImageIds === "function"
? viewport.getImageIds() ? viewport.getImageIds()
: []; : [];
total = imageIds.length; total = imageIds.length;
if (typeof viewport.getProperties === "function") { if (typeof viewport.getProperties === "function") {
const props = viewport.getProperties(); const props = viewport.getProperties();
if (props.voiRange) { if (props.voiRange) {
const { lower, upper } = props.voiRange; const { lower, upper } = props.voiRange;
windowCenter = ((upper + lower) / 2).toFixed(0); windowCenter = ((upper + lower) / 2).toFixed(0);
windowWidth = (upper - lower).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 => { setImageInfos(prev => {
const next = [...prev]; const next = [...prev];
next[viewportIdx] = { next[viewportIdx] = {
index: currentIndex + 1, index: currentIndex + 1,
total, total,
windowCenter, windowCenter,
windowWidth, windowWidth,
}; slicePosition, // add this field
return next; };
}); return next;
}, [viewportModes]); });
}, [viewportModes]);
const [gridBtnActive, setGridBtnActive] = useState(false); const [gridBtnActive, setGridBtnActive] = useState(false);
@@ -918,7 +926,6 @@ const updateOverlay = useCallback((viewportIdx: number, viewport: any) => {
} }
if (viewportModes[i] === 'stack') { if (viewportModes[i] === 'stack') {
//if (imageIds && !restoringStateRef.current) {
if (imageIds && imageIds.length > 0) { if (imageIds && imageIds.length > 0) {
viewport.setStack(imageIds); viewport.setStack(imageIds);
} }
@@ -950,6 +957,16 @@ const updateOverlay = useCallback((viewportIdx: number, viewport: any) => {
volume.load(); volume.load();
viewport.setVolumes([{ volumeId }]); viewport.setVolumes([{ volumeId }]);
viewport.render(); 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 continue
} }
viewport.render(); viewport.render();
@@ -991,7 +1008,6 @@ const updateOverlay = useCallback((viewportIdx: number, viewport: any) => {
} }
// Always (re-)attach overlay update for each viewport // Always (re-)attach overlay update for each viewport
if ((element)._overlayListener) { if ((element)._overlayListener) {
// Remove previous listener if present // Remove previous listener if present
@@ -1001,8 +1017,14 @@ const updateOverlay = useCallback((viewportIdx: number, viewport: any) => {
viewport.element.addEventListener(IMAGE_RENDERED, overlayListener); viewport.element.addEventListener(IMAGE_RENDERED, overlayListener);
(element)._overlayListener = overlayListener; (element)._overlayListener = overlayListener;
updateOverlay(i, viewport); updateOverlay(i, viewport);
} }
const activeVolumeViewports = []; const activeVolumeViewports = [];
for (let i = 0; i < viewportGrid.rows * viewportGrid.cols; i++) { for (let i = 0; i < viewportGrid.rows * viewportGrid.cols; i++) {
const viewportId = `CT_${i}`; const viewportId = `CT_${i}`;
@@ -1369,17 +1391,17 @@ const updateOverlay = useCallback((viewportIdx: number, viewport: any) => {
} }
// Attach to window with a unique name // Attach to window with a unique name
window[`getViewport_${apiKey}`] = (idx: number) => { window[`getViewport_${apiKey}`] = (idx: number) => {
const renderingEngine = renderingEngineRef.current; const renderingEngine = renderingEngineRef.current;
if (!renderingEngine) return undefined; if (!renderingEngine) return undefined;
return renderingEngine.getViewport(`CT_${idx}`); return renderingEngine.getViewport(`CT_${idx}`);
}; };
window[`getAllViewports_${apiKey}`] = () => { window[`getAllViewports_${apiKey}`] = () => {
const renderingEngine = renderingEngineRef.current; const renderingEngine = renderingEngineRef.current;
if (!renderingEngine) return []; if (!renderingEngine) return [];
// Return all currently active viewports as an array // Return all currently active viewports as an array
return Array.from({ length: MAX_VIEWPORTS }, (_, i) => renderingEngine.getViewport(`CT_${i}`)).filter(Boolean); return Array.from({ length: MAX_VIEWPORTS }, (_, i) => renderingEngine.getViewport(`CT_${i}`)).filter(Boolean);
}; };
window[`jumpToSliceByImageId_${apiKey}`] = jumpToSliceByImageId; window[`jumpToSliceByImageId_${apiKey}`] = jumpToSliceByImageId;
window[`resetViewer_${apiKey}`] = resetViewer; window[`resetViewer_${apiKey}`] = resetViewer;
@@ -1454,9 +1476,9 @@ const updateOverlay = useCallback((viewportIdx: number, viewport: any) => {
console.log("viewport", viewportId, "properties", p); console.log("viewport", viewportId, "properties", p);
// --- Volume orientation and slice index --- // --- Volume orientation and slice index ---
if (modes[i] === "volume") { if (modes[i] === "volume") {
console.log("Getting orientation for viewport", i); console.log("Getting orientation for viewport", i);
console.log("viewport.viewportProperties", viewport.viewportProperties); console.log("viewport.viewportProperties", viewport.viewportProperties);
o = viewport.viewportProperties?.orientation || null; o = viewport.viewportProperties?.orientation || null;
if (typeof viewport.getSliceIndex === "function") { if (typeof viewport.getSliceIndex === "function") {
vIdx = viewport.getSliceIndex(); vIdx = viewport.getSliceIndex();
} }
@@ -1528,19 +1550,19 @@ const updateOverlay = useCallback((viewportIdx: number, viewport: any) => {
console.log("viewport.getImageIds", viewport.getImageIds?.()); console.log("viewport.getImageIds", viewport.getImageIds?.());
if (!importedImageIds.length) continue; if (!importedImageIds.length) continue;
try { try {
const viewportImageIds = viewport.getImageIds?.() || []; const viewportImageIds = viewport.getImageIds?.() || [];
if ( if (
viewportImageIds.length !== importedImageIds.length || viewportImageIds.length !== importedImageIds.length ||
!viewportImageIds.every((id, idx) => id === importedImageIds[idx]) !viewportImageIds.every((id, idx) => id === importedImageIds[idx])
) { ) {
console.warn( console.warn(
`Viewport ${i}: importedImageIds do not match viewportImageIds`, `Viewport ${i}: importedImageIds do not match viewportImageIds`,
{ importedImageIds, viewportImageIds } { importedImageIds, viewportImageIds }
); );
allReady = false; allReady = false;
break; break;
} }
} catch(e) { } catch (e) {
allReady = false; allReady = false;
} }
@@ -2371,8 +2393,12 @@ const updateOverlay = useCallback((viewportIdx: number, viewport: any) => {
</div> </div>
</> </>
) : ( ) : (
// Volume mode: do not show image position
<div> <div>
Slice: {imageInfos[i]?.index} / {imageInfos[i]?.total}
{imageInfos[i]?.slicePosition && (
<span> &nbsp; ({imageInfos[i]?.slicePosition})</span>
)}
<br />
WC: {imageInfos[i]?.windowCenter} &nbsp; WW: {imageInfos[i]?.windowWidth} WC: {imageInfos[i]?.windowCenter} &nbsp; WW: {imageInfos[i]?.windowWidth}
</div> </div>
)} )}