From 1fd3020f0cef24b32cac179230719dfa7388a5af Mon Sep 17 00:00:00 2001 From: Ross Date: Sun, 1 Jun 2025 22:17:41 +0100 Subject: [PATCH] improve export schema --- dicom-viewer/src/App.tsx | 235 +++++++++++++++++++-------------------- 1 file changed, 114 insertions(+), 121 deletions(-) diff --git a/dicom-viewer/src/App.tsx b/dicom-viewer/src/App.tsx index 7ed4e24..6fd60cf 100644 --- a/dicom-viewer/src/App.tsx +++ b/dicom-viewer/src/App.tsx @@ -1073,134 +1073,127 @@ function App() { } useEffect(() => { // Export the current viewer state as JSON - window.exportViewerState = () => { - const numActive = viewportGrid.rows * viewportGrid.cols; - const activeViewportModes = viewportModes.slice(0, numActive); - const activeSelectedStackIdx = selectedStackIdx.slice(0, numActive); - const activeAvailableStacks = availableStacks; - const activeViewportImageIds = viewportImageIds.slice(0, numActive); +window.exportViewerState = () => { + const numActive = viewportGrid.rows * viewportGrid.cols; + const modes = viewportModes.slice(0, numActive); + const stackIdx = selectedStackIdx.slice(0, numActive); + const stacks = availableStacks.map(s => ({ + i: s.imageIds, // imageIds + u: s.studyInstanceUID // studyInstanceUID + })); - // Collect viewport properties, stack positions, and camera for active viewports - const renderingEngine = renderingEngineRef.current; - const viewportProperties: any[] = []; - const stackPositions: number[] = []; - const cameraProperties: any[] = []; - for (let i = 0; i < numActive; i++) { - let props = null; - let stackPos = null; - let camera = null; - if (renderingEngine) { - const viewportId = `CT_${i}`; - const viewport = renderingEngine.getViewport(viewportId); - if (viewport) { - if (typeof viewport.getProperties === "function") { - props = viewport.getProperties(); - } - if (typeof viewport.getCurrentImageIdIndex === "function") { - stackPos = viewport.getCurrentImageIdIndex(); - } - if (typeof viewport.getCamera === "function") { - camera = viewport.getCamera(); - } + // Only keep minimal properties + const renderingEngine = renderingEngineRef.current; + const props: any[] = []; + const stackPos: number[] = []; + const cam: any[] = []; + for (let i = 0; i < numActive; i++) { + let p = null, s = null, c = null; + if (renderingEngine) { + const viewportId = `CT_${i}`; + const viewport = renderingEngine.getViewport(viewportId); + if (viewport) { + if (typeof viewport.getProperties === "function") { + const { voiRange, invert, interpolationType, VOILUTFunction, colormap } = viewport.getProperties(); + p = { voiRange, invert, interpolationType, VOILUTFunction, colormap }; + } + if (typeof viewport.getCurrentImageIdIndex === "function") { + s = viewport.getCurrentImageIdIndex(); + } + if (typeof viewport.getCamera === "function") { + c = viewport.getCamera(); } } - viewportProperties.push(props); - stackPositions.push(stackPos); - cameraProperties.push(camera); + } + props.push(p); + stackPos.push(s); + cam.push(c); + } + + // Use short keys for compactness + const state = { + grid: viewportGrid, + modes, + stacks, + stackIdx, + stackPos, + props, + cam, + }; + return JSON.stringify(state); +}; + + // Import and restore a viewer state from JSON (using stack indices) +window.importViewerState = (json: string) => { + try { + restoringStateRef.current = true; + const state = typeof json === "string" ? JSON.parse(json) : json; + if (state.grid) setViewportGrid(state.grid); + if (state.modes) setViewportModes(state.modes); + if (state.stacks) setAvailableStacks(state.stacks.map((s: any) => ({ + imageIds: s.i, + studyInstanceUID: s.u, + }))); + if (state.stackIdx && state.stacks) { + const reconstructed = state.stackIdx.map( + (idx: number) => state.stacks[idx]?.i || [] + ); + setViewportImageIds(reconstructed); + setSelectedStackIdx(state.stackIdx); } - const state = { - viewportGrid, - viewportModes: activeViewportModes, - selectedStackIdx: activeSelectedStackIdx, - availableStacks: activeAvailableStacks, - viewportImageIds: activeViewportImageIds, - viewportProperties, - stackPositions, - cameraProperties, - }; - return JSON.stringify(state, null, 2); - }; - - window.importViewerState = (json: string) => { - try { - console.log("Importing viewer state:", json); - restoringStateRef.current = true; - - const state = typeof json === "string" ? JSON.parse(json) : json; - if (state.viewportGrid) setViewportGrid(state.viewportGrid); - if (state.viewportModes) setViewportModes(state.viewportModes); - if (state.selectedStackIdx) setSelectedStackIdx(state.selectedStackIdx); - if (state.availableStacks) setAvailableStacks(state.availableStacks); - if (state.viewportImageIds) setViewportImageIds(state.viewportImageIds); - - setTimeout(() => { - const renderingEngine = renderingEngineRef.current; - if (!renderingEngine) { - restoringStateRef.current = false; - return; - } - const numActive = state.viewportGrid?.rows * state.viewportGrid?.cols || 1; - for (let i = 0; i < numActive; i++) { - const viewportId = `CT_${i}`; - const viewport = renderingEngine.getViewport(viewportId); - if (!viewport) continue; - - // Restore stack position (scroll) - if ( - state.stackPositions && - typeof state.stackPositions[i] === "number" && - typeof viewport.setImageIdIndex === "function" - ) { - if (csUtilities && csUtilities.jumpToSlice) { - csUtilities.jumpToSlice(viewport.element, { imageIndex: state.stackPositions[i] }); - } else { - viewport.setImageIdIndex(state.stackPositions[i]); - } - } - - // Restore viewport properties (VOI, invert, etc.) - if ( - state.viewportProperties && - state.viewportProperties[i] && - typeof viewport.setProperties === "function" - ) { - const props = state.viewportProperties[i]; - const { voiRange, invert, interpolationType, VOILUTFunction, colormap } = props; - const propsToRestore: any = {}; - if (voiRange && voiRange.lower !== undefined && voiRange.upper !== undefined) propsToRestore.voiRange = voiRange; - if (invert !== undefined) propsToRestore.invert = invert; - if (interpolationType !== undefined) propsToRestore.interpolationType = interpolationType; - if (VOILUTFunction) propsToRestore.VOILUTFunction = VOILUTFunction; - if ( - colormap !== undefined && - colormap !== null && - (typeof colormap === "string" || (typeof colormap === "object" && !("actor" in colormap))) - ) { - propsToRestore.colormap = colormap; - } - viewport.setProperties(propsToRestore); - viewport.render(); - } - - // Restore camera properties (zoom, pan, orientation) - if ( - state.cameraProperties && - state.cameraProperties[i] && - typeof viewport.setCamera === "function" - ) { - viewport.setCamera(state.cameraProperties[i]); - viewport.render(); - } - } + setTimeout(() => { + const renderingEngine = renderingEngineRef.current; + if (!renderingEngine) { restoringStateRef.current = false; - console.log("Viewer state restored successfully"); - }, 300); - } catch (e) { + return; + } + const numActive = state.grid?.rows * state.grid?.cols || 1; + for (let i = 0; i < numActive; i++) { + const viewportId = `CT_${i}`; + const viewport = renderingEngine.getViewport(viewportId); + if (!viewport) continue; + + // Restore stack position + if ( + state.stackPos && + typeof state.stackPos[i] === "number" && + typeof viewport.setImageIdIndex === "function" + ) { + if (csUtilities && csUtilities.jumpToSlice) { + csUtilities.jumpToSlice(viewport.element, { imageIndex: state.stackPos[i] }); + } else { + viewport.setImageIdIndex(state.stackPos[i]); + } + } + + // Restore properties + if ( + state.props && + state.props[i] && + typeof viewport.setProperties === "function" + ) { + viewport.setProperties(state.props[i]); + viewport.render(); + } + + // Restore camera + if ( + state.cam && + state.cam[i] && + typeof viewport.setCamera === "function" + ) { + viewport.setCamera(state.cam[i]); + viewport.render(); + } + } restoringStateRef.current = false; - alert("Failed to import viewer state: " + e); - } - }; + }, 300); + } catch (e) { + restoringStateRef.current = false; + alert("Failed to import viewer state: " + e); + } +}; // Export all annotations using cornerstoneTools.annotation.state.getAllAnnotations() window.exportAnnotations = () => {