From b408ba96b3127ae825173f617ee376d358a2cd18 Mon Sep 17 00:00:00 2001 From: Ross Date: Mon, 2 Jun 2025 09:59:51 +0100 Subject: [PATCH] fix tool import --- dicom-viewer/src/App.tsx | 272 ++++++++++++++++++++++----------------- 1 file changed, 155 insertions(+), 117 deletions(-) diff --git a/dicom-viewer/src/App.tsx b/dicom-viewer/src/App.tsx index 6fd60cf..d73174c 100644 --- a/dicom-viewer/src/App.tsx +++ b/dicom-viewer/src/App.tsx @@ -1073,132 +1073,152 @@ function App() { } useEffect(() => { // Export the current viewer state as JSON -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 - })); + 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 + })); - // 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(); - } - } - } - 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); - } - - setTimeout(() => { + // Only keep minimal properties const renderingEngine = renderingEngineRef.current; - if (!renderingEngine) { - restoringStateRef.current = false; - return; - } - const numActive = state.grid?.rows * state.grid?.cols || 1; + const props: any[] = []; + const stackPos: number[] = []; + const cam: any[] = []; 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]); + 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(); + } } } - - // 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(); - } + props.push(p); + stackPos.push(s); + cam.push(c); } - restoringStateRef.current = false; - }, 300); - } catch (e) { - restoringStateRef.current = false; - alert("Failed to import viewer state: " + e); - } -}; + + // 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); + } + + setTimeout(() => { + const renderingEngine = renderingEngineRef.current; + if (!renderingEngine) { + restoringStateRef.current = false; + 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; + }, 300); + } catch (e) { + restoringStateRef.current = false; + alert("Failed to import viewer state: " + e); + } + }; // Export all annotations using cornerstoneTools.annotation.state.getAllAnnotations() window.exportAnnotations = () => { try { - const allAnnotations = cornerstoneTools.annotation.state.getAllAnnotations(); + let allAnnotations = cornerstoneTools.annotation.state.getAllAnnotations(); + console.log("Exporting annotations:", allAnnotations); + // Remove ReferenceLines annotations (toolName may be 'ReferenceLines') + if (Array.isArray(allAnnotations)) { + // Only keep annotations with toolName in the allowed list + const allowedTools = [ + LengthTool.toolName, + ArrowAnnotateTool.toolName, + RectangleROITool.toolName, + EllipticalROITool.toolName, + PlanarFreehandROITool.toolName, + PlanarFreehandContourSegmentationTool.toolName, + ProbeTool.toolName, + SculptorTool.toolName, + ]; + allAnnotations = allAnnotations.filter( + ann => allowedTools.includes(ann.metadata.toolName) + ); + } else { + return "{}"; // If no annotations found, return empty object + } return JSON.stringify(allAnnotations, null, 2); } catch (e) { alert("Failed to export annotations: " + e); @@ -1210,7 +1230,25 @@ window.importViewerState = (json: string) => { window.importAnnotations = (json: string) => { try { const annotationData = typeof json === "string" ? JSON.parse(json) : json; - cornerstoneTools.annotation.state.restoreAnnotations(annotationData); + // Remove all existing annotations if needed + if (cornerstoneTools.annotation.state.removeAllAnnotations) { + cornerstoneTools.annotation.state.removeAllAnnotations(); + } + // Add each annotation from the imported data + if (Array.isArray(annotationData)) { + annotationData.forEach(ann => { + cornerstoneTools.annotation.state.addAnnotation(ann); + }); + } else if (annotationData && typeof annotationData === "object") { + // If the data is an object with tool types as keys + Object.values(annotationData).forEach((anns: any) => { + if (Array.isArray(anns)) { + anns.forEach(ann => { + cornerstoneTools.annotation.state.addAnnotation(ann); + }); + } + }); + } if (renderingEngineRef.current) { renderingEngineRef.current.render(); }