From 779559997e00c8df563a00f99006822af2ab2fd2 Mon Sep 17 00:00:00 2001 From: Ross Date: Mon, 8 Sep 2025 14:00:19 +0100 Subject: [PATCH] Enhance DICOM viewer interaction by adding context menu prevention and fullscreen support; register basic interaction tools for improved user experience --- anatomy/static/js/anatomy.js | 173 +++++++++++++---------------------- 1 file changed, 64 insertions(+), 109 deletions(-) diff --git a/anatomy/static/js/anatomy.js b/anatomy/static/js/anatomy.js index a033406c..e30558cd 100644 --- a/anatomy/static/js/anatomy.js +++ b/anatomy/static/js/anatomy.js @@ -536,18 +536,39 @@ async function setUpDicom3d(element) { element.style.position = element.style.position || "relative"; element.innerHTML = ""; // clear + // Disable right-click context menu on the element + element.addEventListener('contextmenu', function (e) { + e.preventDefault(); + }); + + element.addEventListener('dblclick', function (e) { + if (element.requestFullscreen) { + element.requestFullscreen(); + } else if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen(); + } else if (element.mozRequestFullScreen) { + element.mozRequestFullScreen(); + } else if (element.msRequestFullscreen) { + element.msRequestFullscreen(); + } + }); + // create a unique rendering engine / viewport ids per element const renderingEngineId = `rendingEngine`; - const viewportId = `CT_AXIAL-STACK`; + const viewportId = `1`; try { // dynamic import of cornerstone3D core + dicom loader const coreModule = window.cornerstone3d const dicomLoaderModule = window.cornerstoneDICOMImageLoader + const toolsModule = window.cornerstone3dTools; await coreModule.init(); await dicomLoaderModule.init(); + + await toolsModule.init(); + const { RenderingEngine, Enums } = coreModule; const { chunkManager } = dicomLoaderModule; // may exist for some setups @@ -566,6 +587,46 @@ async function setUpDicom3d(element) { const viewport = renderingEngine.getViewport(viewportId); + + // --- Register basic interaction tools (best-effort with @cornerstonejs/tools) --- + // dynamic import so bundlers/CDNs provide the package + // prefer module import, fall back to global if present + + const { ToolGroupManager } = toolsModule; + + toolsModule.addTool(toolsModule.PanTool) + toolsModule.addTool(toolsModule.WindowLevelTool) + //toolsModule.addTool(toolsModule.StackScrollTool) + toolsModule.addTool(toolsModule.ZoomTool) + + + + const toolGroupId = `toolgroup-${viewportId}`; + console.log("ToolGroup id", toolGroupId); + console.log("ToolGroupManager", ToolGroupManager); + if (!ToolGroupManager.getToolGroup(toolGroupId)) { + ToolGroupManager.createToolGroup(toolGroupId); + } + const toolGroup = ToolGroupManager.getToolGroup(toolGroupId); + + toolGroup.addTool(toolsModule.PanTool.toolName); + toolGroup.addTool(toolsModule.WindowLevelTool.toolName); + //toolGroup.addTool(toolsModule.StackScrollTool.toolName); + toolGroup.addTool(toolsModule.ZoomTool.toolName); + + // use actual tool names if available, else fall back to common strings + //const stackName = (StackScrollTool && (StackScrollTool.toolName || StackScrollTool.name)) || 'StackScroll'; + + //toolGroup.setToolEnabled(toolsModule.PanTool.toolName, { bindings: [{ mouseButton: 1 }] }); + toolGroup.setToolActive(toolsModule.PanTool.toolName, { bindings: [{ mouseButton: 1 }] }); + toolGroup.setToolActive(toolsModule.WindowLevelTool.toolName, { bindings: [{ mouseButton: 2 }] }); + toolGroup.setToolActive(toolsModule.ZoomTool.toolName, { bindings: [{ mouseButton: 4 }] }); + toolGroup.setToolActive(toolsModule.ZoomTool.toolName, { bindings: [{ mouseButton: 524288 }] }); + toolGroup.addViewport(viewportId, renderingEngineId); + + window.toolGroup = toolGroup; // expose for debugging + + // Prepare imageIds: ensure remote URLs are usable by dicom-image-loader (wadouri:) const imageIds = images.map((url) => { url = url.trim(); @@ -591,6 +652,7 @@ async function setUpDicom3d(element) { viewport.render(); + // store references for debugging/use elsewhere element._cornerstone3d = { renderingEngineId, @@ -613,114 +675,7 @@ async function setUpDicom3d(element) { window.setUpDicom = setUpDicom3d; -//function keyUpHandler(e) { -// if (e.key == "Control") { -// dicomViewer.registerPrimaryDicomInterface(e); -// window.control_pressed = false; -// } -//} -// -//function keyDownHandler(e) { -// // Ignore our custom keybindings if we are currently in a field that -// // accepts some kind of input -// if ($("*:focus:not(disabled)").is("textarea, input")) { -// // unless a modifier key is pressed (not shift) -// if (e.altKey ? true : false || e.ctrlKey ? true : false) {} else { -// return; -// } -// } -// -// if (e.key == "Control") { -// if (window.control_pressed == false) { -// window.control_pressed = true; -// dicomViewer.registerAltDicomInterface(e); -// } -// } -// -// var charCode = typeof e.which == "number" ? e.which : e.keyCode; -// console.log(e, charCode); -// -// function numberKeyPressed(e, x) { -// if (e.altKey ? true : false) { -// dicomViewer.selectThumb(x); -// e.preventDefault(); -// } else {} -// } -// -// switch (charCode) { -// case 13: // Return -// if (e.shiftKey ? true : false) { -// //$(".next-button:last").click(); -// } else { -// //$(".check-button:last").click(); -// } -// break; -// case 32: // Space -// if (e.shiftKey ? true : false) { -// //$(".previous-button:last").click(); -// } else { -// //$(".next-button:last").click(); -// } -// e.preventDefault(); // Needed to stop the default action (scroll) -// break; -// case 46: // . -// //toggleFlagged(); -// break; -// -// // Numbers 1-9 select the corresponding answer (if it exists) -// // TODO: fix for multi question questions -// case 49: // 1 -// numberKeyPressed(e, 0); -// break; -// case 50: // 2 -// numberKeyPressed(e, 1); -// break; -// case 51: // 3 -// numberKeyPressed(e, 2); -// break; -// case 52: // 4 -// numberKeyPressed(e, 3); -// break; -// case 53: // 5 -// numberKeyPressed(e, 4); -// break; -// case 54: // 6 -// numberKeyPressed(e, 5); -// break; -// case 55: // 7 -// numberKeyPressed(e, 6); -// break; -// case 56: // 8 -// numberKeyPressed(e, 7); -// break; -// case 57: // 9 -// numberKeyPressed(e, 8); -// break; -// case 72: // H -// previousQuestion(); -// break; -// case 76: // L -// //nextQuestion(); -// break; -// -// case 102: // f -// //$("#filter-toggle").click(); -// break; -// case 103: // g -// //$("#options").slideDown("slow"); -// //$("#goto-question-input").focus(); -// //e.preventDefault(); -// break; -// -// // Vim like scrolling (incredibly important) -// case 106: // j -// //window.scrollBy(0, 25); -// break; -// case 107: // k -// //window.scrollBy(0, -25); -// break; -// } -//} + function create_popup_window(url, title) {