From 3f2968b05f1aca51b462a43abfbf9831f580f5b9 Mon Sep 17 00:00:00 2001 From: Evren Ozkan Date: Fri, 26 Oct 2018 23:45:19 -0400 Subject: [PATCH] Integrate Cornerstone Tools v3 (#235) - WIP fix(mouse-buttons): Support for assigning tools to mouse buttons (left, right, middle) fix(tool-naming): Fix tool naming issues --- .../toolbarSection/toolbarSection.js | 45 +++---- .../toolbarSection/toolbarSection.js | 32 ++--- OHIFViewer/client/components/viewer/viewer.js | 7 +- Packages/ohif-cornerstone/package.js | 4 +- .../imageViewerViewport.js | 4 +- .../textMarkerDialogs/textMarkerDialogs.js | 4 +- .../ohif-viewerbase/client/lib/hotkeyUtils.js | 59 +++++----- .../ohif-viewerbase/client/lib/toolManager.js | 110 ++++++++++++------ .../toolbarSection/toolbarSection.js | 26 ++--- 9 files changed, 170 insertions(+), 121 deletions(-) diff --git a/LesionTracker/client/components/toolbarSection/toolbarSection.js b/LesionTracker/client/components/toolbarSection/toolbarSection.js index db0845e43..8b031213d 100644 --- a/LesionTracker/client/components/toolbarSection/toolbarSection.js +++ b/LesionTracker/client/components/toolbarSection/toolbarSection.js @@ -288,28 +288,29 @@ Template.toolbarSection.onCreated( function() { }); Template.toolbarSection.onRendered(function() { - // Set disabled/enabled tool buttons that are set in toolManager - const states = Viewerbase.toolManager.getToolDefaultStates(); - const disabledToolButtons = states.disabledToolButtons; - const allToolbarButtons = $('.toolbarSection').find('.toolbarSectionButton:not(.nonAutoDisableState)'); - - // Additional toolbar buttons whose classes are not toolbarSectionButton - allToolbarButtons.push($('#toolbarSectionEntry')[0]); - allToolbarButtons.push($('#toggleMeasurements')[0]); - - if (disabledToolButtons && disabledToolButtons.length > 0) { - for (let i = 0; i < allToolbarButtons.length; i++) { - const toolbarButton = allToolbarButtons[i]; - const index = disabledToolButtons.indexOf($(toolbarButton).attr('id')); - if (index !== -1) { - $(toolbarButton).addClass('disabled'); - $(toolbarButton).find('*').addClass('disabled'); - } else { - $(toolbarButton).removeClass('disabled'); - $(toolbarButton).find('*').removeClass('disabled'); - } - } - } + // TODO: Figure out a way to disable/enable the buttons with each status from New API + // // Set disabled/enabled tool buttons that are set in toolManager + // const states = Viewerbase.toolManager.getToolDefaultStates(); + // const disabledToolButtons = states.disabledToolButtons; + // const allToolbarButtons = $('.toolbarSection').find('.toolbarSectionButton:not(.nonAutoDisableState)'); + // + // // Additional toolbar buttons whose classes are not toolbarSectionButton + // allToolbarButtons.push($('#toolbarSectionEntry')[0]); + // allToolbarButtons.push($('#toggleMeasurements')[0]); + // + // if (disabledToolButtons && disabledToolButtons.length > 0) { + // for (let i = 0; i < allToolbarButtons.length; i++) { + // const toolbarButton = allToolbarButtons[i]; + // const index = disabledToolButtons.indexOf($(toolbarButton).attr('id')); + // if (index !== -1) { + // $(toolbarButton).addClass('disabled'); + // $(toolbarButton).find('*').addClass('disabled'); + // } else { + // $(toolbarButton).removeClass('disabled'); + // $(toolbarButton).find('*').removeClass('disabled'); + // } + // } + // } }); Template.caseProgress.onDestroyed(() => { diff --git a/OHIFViewer/client/components/toolbarSection/toolbarSection.js b/OHIFViewer/client/components/toolbarSection/toolbarSection.js index 4c21fdea6..433eb5803 100644 --- a/OHIFViewer/client/components/toolbarSection/toolbarSection.js +++ b/OHIFViewer/client/components/toolbarSection/toolbarSection.js @@ -303,20 +303,20 @@ Template.toolbarSection.onRendered(function() { OHIF.viewerbase.viewportUtils.toggleCineDialog(); } - // Set disabled/enabled tool buttons that are set in toolManager - - const states = OHIF.viewerbase.toolManager.getToolDefaultStates(); - const disabledToolButtons = states.disabledToolButtons; - const allToolbarButtons = $('#toolbar').find('button'); - if (disabledToolButtons && disabledToolButtons.length > 0) { - for (let i = 0; i < allToolbarButtons.length; i++) { - const toolbarButton = allToolbarButtons[i]; - $(toolbarButton).prop('disabled', false); - - const index = disabledToolButtons.indexOf($(toolbarButton).attr('id')); - if (index !== -1) { - $(toolbarButton).prop('disabled', true); - } - } - } + // TODO: Figure out a way to disable/enable the buttons with each status from New API + // // Set disabled/enabled tool buttons that are set in toolManager + // const states = OHIF.viewerbase.toolManager.getToolDefaultStates(); + // const disabledToolButtons = states.disabledToolButtons; + // const allToolbarButtons = $('#toolbar').find('button'); + // if (disabledToolButtons && disabledToolButtons.length > 0) { + // for (let i = 0; i < allToolbarButtons.length; i++) { + // const toolbarButton = allToolbarButtons[i]; + // $(toolbarButton).prop('disabled', false); + // + // const index = disabledToolButtons.indexOf($(toolbarButton).attr('id')); + // if (index !== -1) { + // $(toolbarButton).prop('disabled', true); + // } + // } + // } }); diff --git a/OHIFViewer/client/components/viewer/viewer.js b/OHIFViewer/client/components/viewer/viewer.js index 6b33390af..db0acf46e 100644 --- a/OHIFViewer/client/components/viewer/viewer.js +++ b/OHIFViewer/client/components/viewer/viewer.js @@ -48,7 +48,12 @@ Meteor.startup(() => { Session.setDefault('leftSidebar', false); Session.setDefault('rightSidebar', false); - OHIF.viewer.defaultTool = 'wwwc'; + OHIF.viewer.defaultTool = { + left: 'wwwc', + right: 'zoom', + middle: 'pan' + }; + OHIF.viewer.refLinesEnabled = true; OHIF.viewer.cine = { framesPerSecond: 24, diff --git a/Packages/ohif-cornerstone/package.js b/Packages/ohif-cornerstone/package.js index 31a7443d0..06e62f875 100644 --- a/Packages/ohif-cornerstone/package.js +++ b/Packages/ohif-cornerstone/package.js @@ -6,8 +6,8 @@ Package.describe({ Npm.depends({ hammerjs: '2.0.8', - 'cornerstone-core': '2.2.6', - 'cornerstone-tools': '3.0.0-b.969', + 'cornerstone-core': '2.2.7', + 'cornerstone-tools': '3.0.0-b.1020', 'cornerstone-math': '0.1.6', 'dicom-parser': '1.8.0', 'cornerstone-wado-image-loader': '2.1.4', diff --git a/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js b/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js index 80028e90c..665fbdeab 100644 --- a/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js +++ b/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js @@ -272,8 +272,7 @@ const loadDisplaySetIntoViewport = (data, templateData) => { // Use the tool manager to enable the currently active tool for this // newly rendered element - const activeTool = toolManager.getActiveTool(); - toolManager.setActiveTool(activeTool); + toolManager.setActiveTool(); // Define a function to run whenever the Cornerstone viewport is rendered // (e.g. following a change of window or zoom) @@ -453,6 +452,7 @@ const loadDisplaySetIntoViewport = (data, templateData) => { // that is used for its synchronized viewport updating. // This is necessary if this new image shares a frame of reference // with currently displayed images + const activeTool = toolManager.getActiveTool(); if (activeTool === 'crosshairs') { updateCrosshairsSynchronizer(imagePlane.frameOfReferenceUID); } diff --git a/Packages/ohif-viewerbase/client/components/viewer/textMarkerDialogs/textMarkerDialogs.js b/Packages/ohif-viewerbase/client/components/viewer/textMarkerDialogs/textMarkerDialogs.js index 335418881..3ca295764 100644 --- a/Packages/ohif-viewerbase/client/components/viewer/textMarkerDialogs/textMarkerDialogs.js +++ b/Packages/ohif-viewerbase/client/components/viewer/textMarkerDialogs/textMarkerDialogs.js @@ -50,10 +50,10 @@ Template.textMarkerDialogs.events({ cornerstone.updateImage(element); }, 'click .closeTextMarkerDialogs'() { - const defaultTool = toolManager.getDefaultTool(); - toolManager.setActiveTool(defaultTool); + toolManager.setActiveTool(); document.getElementById('textMarkerOptionsDialog').close(); $('#spine').removeClass('active'); + const defaultTool = toolManager.getDefaultTool(); $('#' + defaultTool).addClass('active'); } diff --git a/Packages/ohif-viewerbase/client/lib/hotkeyUtils.js b/Packages/ohif-viewerbase/client/lib/hotkeyUtils.js index 59c39c61d..4aca22bdf 100644 --- a/Packages/ohif-viewerbase/client/lib/hotkeyUtils.js +++ b/Packages/ohif-viewerbase/client/lib/hotkeyUtils.js @@ -13,7 +13,13 @@ import { WLPresets } from './WLPresets'; // TODO: add this to namespace definitions Meteor.startup(function() { OHIF.viewer.loadIndicatorDelay = 200; - OHIF.viewer.defaultTool = 'Wwwc'; + + OHIF.viewer.defaultTool = { + left: 'wwwc', + right: 'zoom', + middle: 'pan' + }; + OHIF.viewer.refLinesEnabled = true; OHIF.viewer.isPlaying = {}; OHIF.viewer.cine = { @@ -24,17 +30,17 @@ Meteor.startup(function() { OHIF.viewer.defaultHotkeys = { // Tool hotkeys defaultTool: 'ESC', - Zoom: 'Z', - Wwwc: 'W', - Pan: 'P', - Angle: 'A', - StackScroll: 'S', - Magnify: 'M', - Length: '', - Annotate: '', - DragProbe: '', - EllipticalRoi: '', - RectangleRoi: '', + zoom: 'Z', + wwwc: 'W', + pan: 'P', + angle: 'A', + stackScroll: 'S', + magnify: 'M', + length: '', + annotate: '', + dragProbe: '', + ellipticalRoi: '', + rectangleRoi: '', // Viewport hotkeys flipH: 'H', @@ -102,25 +108,24 @@ Meteor.startup(function() { // Register the default tool command OHIF.commands.register(contextName, 'defaultTool', { name: 'Default Tool', - action: () => toolManager.setActiveTool(toolManager.getDefaultTool()) + action: () => toolManager.setActiveTool() }); // Register the tool switching commands registerToolCommands({ - Wwwc: 'W/L', - Zoom: 'Zoom', - Angle: 'Angle Measurement', - DragProbe: 'Pixel Probe', - StackScroll: 'Stack Scroll', - EllipticalRoi: 'Elliptical ROI', - RectangleRoi: 'Rectangle ROI', - Magnify: 'Magnify', - ArrowAnnotate: 'Annotate', - stackScrollMouseWheel: 'Scroll Stack Mouse Wheel', - Pan: 'Pan', - Length: 'Length Measurement', - WwwcRegion: 'W/L by Region', - Crosshairs: 'Crosshairs' + wwwc: 'W/L', + zoom: 'Zoom', + angle: 'Angle Measurement', + dragProbe: 'Pixel Probe', + ellipticalRoi: 'Elliptical ROI', + rectangleRoi: 'Rectangle ROI', + magnify: 'Magnify', + annotate: 'Annotate', + stackScroll: 'Scroll Stack', + pan: 'Pan', + length: 'Length Measurement', + wwwcRegion: 'W/L by Region', + crosshairs: 'Crosshairs' }); // Functions to register the viewport commands diff --git a/Packages/ohif-viewerbase/client/lib/toolManager.js b/Packages/ohif-viewerbase/client/lib/toolManager.js index 046d60412..1f40675d1 100644 --- a/Packages/ohif-viewerbase/client/lib/toolManager.js +++ b/Packages/ohif-viewerbase/client/lib/toolManager.js @@ -4,17 +4,17 @@ import { OHIF } from 'meteor/ohif:core'; import { cornerstone, cornerstoneTools } from 'meteor/ohif:cornerstone'; let activeTool; -let tools = []; +let tools = {}; let initialized = false; let defaultTool = { - left: 'Wwwc', - right: 'Zoom', - middle: 'Pan' + left: 'wwwc', + right: 'zoom', + middle: 'pan' }; const buttonNum = { 'left': 1, - 'right': 4, - 'middle': 2 + 'right': 2, + 'middle': 4 }; /** @@ -24,29 +24,30 @@ export const toolManager = { init() { // if a default tool is globally defined, make it the default tool... if (OHIF.viewer.defaultTool) { - toolManager.setDefaultTool(OHIF.viewer.defaultTool); + toolManager.setDefaultTool(OHIF.viewer.defaultTool.left); + toolManager.setDefaultTool(OHIF.viewer.defaultTool.right, 'right'); + toolManager.setDefaultTool(OHIF.viewer.defaultTool.middle, 'middle'); } cornerstoneTools.init(); - tools = [ - 'Length', - 'Angle', - 'ArrowAnnotate', - 'Wwwc', - 'Zoom', - 'Pan', - 'DragProbe', - 'Magnify', - 'Crosshairs', - 'StackScroll', - 'StackScrollMouseWheel', - 'ZoomTouchPinch', - 'ZoomMouseWheel', - 'EllipticalRoi', - 'RectangleRoi', - 'WwwcRegion' - ]; + tools = { + length: 'LengthTool', + angle: 'AngleTool', + annotate: 'ArrowAnnotateTool', + wwwc: 'WwwcTool', + zoom: 'ZoomTool', + pan: 'PanTool', + dragProbe: 'DragProbeTool', + magnify: 'MagnifyTool', + crosshairs: 'CrosshairsTool', + stackScroll: 'StackScrollTool', + zoomTouchPinch: 'ZoomTouchPinchTool', + zoomMouseWheel: 'ZoomMouseWheelTool', + ellipticalRoi: 'EllipticalRoiTool', + rectangleRoi: 'RectangleRoiTool', + wwwcRegion: 'WwwcRegionTool' + }; initialized = true; }, @@ -88,10 +89,18 @@ export const toolManager = { }, getTools() { - return tools; + return Object.keys(tools); }, setActiveTool(toolName, button = 'left') { + // Using setActiveTool with no arguments activates the default tools for all buttons + if (!toolName) { + toolManager.setActiveTool(toolManager.getDefaultTool()); + toolManager.setActiveTool(toolManager.getDefaultTool('right'), 'right'); + toolManager.setActiveTool(toolManager.getDefaultTool('middle'), 'middle'); + return; + } + let options = {}; const mouseButtonMask = toolManager.getMouseButtonMask(button); if (mouseButtonMask) { @@ -101,9 +110,36 @@ export const toolManager = { } toolManager.setAllToolsPassive(); - cornerstoneTools.setToolActive(toolName, options); - // TODO: add the active tool with the correct button + // Set active tools for the other buttons than this one + switch(button) { + case 'left': + cornerstoneTools.setToolActive(toolManager.getActiveTool('right'), { + mouseButtonMask: toolManager.getMouseButtonMask('right') + }); + cornerstoneTools.setToolActive(toolManager.getActiveTool('middle'), { + mouseButtonMask: toolManager.getMouseButtonMask('middle') + }); + break; + case 'right': + cornerstoneTools.setToolActive(toolManager.getActiveTool('left'), { + mouseButtonMask: toolManager.getMouseButtonMask('left') + }); + cornerstoneTools.setToolActive(toolManager.getActiveTool('middle'), { + mouseButtonMask: toolManager.getMouseButtonMask('middle') + }); + break; + case 'middle': + cornerstoneTools.setToolActive(toolManager.getActiveTool('left'), { + mouseButtonMask: toolManager.getMouseButtonMask('left') + }); + cornerstoneTools.setToolActive(toolManager.getActiveTool('right'), { + mouseButtonMask: toolManager.getMouseButtonMask('right') + }); + break; + } + + cornerstoneTools.setToolActive(toolName, options); activeTool[button] = toolName; // Enable reactivity @@ -111,22 +147,24 @@ export const toolManager = { }, setAllToolsPassive() { - cornerstoneTools.store.state.tools.forEach((tool) => { - cornerstoneTools.setToolPassive(tool.name) - }); + cornerstoneTools.store.state.tools.forEach((tool) => { + cornerstoneTools.setToolPassive(tool.name) + }); }, instantiateTools() { - Array.from(tools).forEach(toolName => { - const apiTool = cornerstoneTools[`${toolName}Tool`]; + Object.keys(tools).forEach(toolName => { + const apiTool = cornerstoneTools[tools[toolName]]; if (apiTool) { - cornerstoneTools.addTool(apiTool); + cornerstoneTools.addTool(apiTool, { name: toolName }); } }); toolManager.setAllToolsPassive(); }, getNearbyToolData() { + // TODO: Implement this and let the others (e.g. deleteLesionKeyboardTool) use this function + // if it does not exist in cornerstoneTools return undefined; }, @@ -152,11 +190,11 @@ export const toolManager = { }, activateCommandButton(button) { - // + // TODO: Do we need this? }, deactivateCommandButton(button) { - // + // TODO: Do we need this? } }; diff --git a/StandaloneViewer/StandaloneViewer/client/components/toolbarSection/toolbarSection.js b/StandaloneViewer/StandaloneViewer/client/components/toolbarSection/toolbarSection.js index 2d47eca2f..f045de55f 100644 --- a/StandaloneViewer/StandaloneViewer/client/components/toolbarSection/toolbarSection.js +++ b/StandaloneViewer/StandaloneViewer/client/components/toolbarSection/toolbarSection.js @@ -47,42 +47,42 @@ Template.toolbarSection.helpers({ const extraTools = []; extraTools.push({ - id: 'Crosshairs', + id: 'crosshairs', title: 'Crosshairs', classes: 'imageViewerTool', iconClasses: 'fa fa-crosshairs' }); extraTools.push({ - id: 'Magnify', + id: 'magnify', title: 'Magnify', classes: 'imageViewerTool toolbarSectionButton', iconClasses: 'fa fa-circle' }); extraTools.push({ - id: 'WwwcRegion', + id: 'wwwcRegion', title: 'ROI Window', classes: 'imageViewerTool', iconClasses: 'fa fa-square' }); extraTools.push({ - id: 'DragProbe', + id: 'dragProbe', title: 'Probe', classes: 'imageViewerTool', iconClasses: 'fa fa-dot-circle-o' }); extraTools.push({ - id: 'EllipticalRoi', + id: 'ellipticalRoi', title: 'Ellipse', classes: 'imageViewerTool', iconClasses: 'fa fa-circle-o' }); extraTools.push({ - id: 'RectangleRoi', + id: 'rectangleRoi', title: 'Rectangle', classes: 'imageViewerTool', iconClasses: 'fa fa-square-o' @@ -126,49 +126,49 @@ Template.toolbarSection.helpers({ const buttonData = []; buttonData.push({ - id: 'StackScroll', + id: 'stackScroll', title: 'Stack Scroll', classes: 'imageViewerTool', iconClasses: 'fa fa-bars' }); buttonData.push({ - id: 'Zoom', + id: 'zoom', title: 'Zoom', classes: 'imageViewerTool', svgLink: 'packages/ohif_viewerbase/assets/icons.svg#icon-tools-zoom' }); buttonData.push({ - id: 'Wwwc', + id: 'wwwc', title: 'Levels', classes: 'imageViewerTool', svgLink: 'packages/ohif_viewerbase/assets/icons.svg#icon-tools-levels' }); buttonData.push({ - id: 'Pan', + id: 'pan', title: 'Pan', classes: 'imageViewerTool', svgLink: 'packages/ohif_viewerbase/assets/icons.svg#icon-tools-pan' }); buttonData.push({ - id: 'Length', + id: 'length', title: 'Length', classes: 'imageViewerTool toolbarSectionButton', svgLink: 'packages/ohif_viewerbase/assets/icons.svg#icon-tools-measure-temp' }); buttonData.push({ - id: 'ArrowAnnotate', + id: 'annotate', title: 'Annotate', classes: 'imageViewerTool', svgLink: 'packages/ohif_viewerbase/assets/icons.svg#icon-tools-measure-non-target' }); buttonData.push({ - id: 'Angle', + id: 'angle', title: 'Angle', classes: 'imageViewerTool', iconClasses: 'fa fa-angle-left'