From 9496410e55acba18a9aa60b0577a5bbab221b0e5 Mon Sep 17 00:00:00 2001 From: Erik Ziegler Date: Sat, 12 Nov 2016 21:06:21 +0100 Subject: [PATCH] LT-324: Improve scrolling responsiveness --- OHIFViewer/client/components/viewer/viewer.js | 1 - .../client/cornerstoneTools.js | 1065 +++++++++++++---- .../imageViewerViewport.html | 2 +- .../imageViewerViewport.js | 30 +- .../loadingIndicator/loadingIndicator.js | 1 + Packages/ohif-viewerbase/lib/enableHotkeys.js | 2 +- .../client/components/viewer/viewer.js | 8 - 7 files changed, 878 insertions(+), 231 deletions(-) diff --git a/OHIFViewer/client/components/viewer/viewer.js b/OHIFViewer/client/components/viewer/viewer.js index 042497f48..0618b0ef5 100644 --- a/OHIFViewer/client/components/viewer/viewer.js +++ b/OHIFViewer/client/components/viewer/viewer.js @@ -1,7 +1,6 @@ import { OHIF } from 'meteor/ohif:core'; OHIF.viewer = OHIF.viewer || {}; -OHIF.viewer.loadIndicatorDelay = 500; OHIF.viewer.defaultTool = 'wwwc'; OHIF.viewer.refLinesEnabled = true; OHIF.viewer.cine = { diff --git a/Packages/ohif-cornerstone/client/cornerstoneTools.js b/Packages/ohif-cornerstone/client/cornerstoneTools.js index 35447759c..dbfd8bddc 100644 --- a/Packages/ohif-cornerstone/client/cornerstoneTools.js +++ b/Packages/ohif-cornerstone/client/cornerstoneTools.js @@ -1,4 +1,4 @@ -/*! cornerstoneTools - v0.7.9 - 2016-09-18 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstoneTools */ +/*! cornerstoneTools - v0.7.9 - 2016-10-29 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstoneTools */ // Begin Source: src/header.js if (typeof cornerstone === 'undefined') { cornerstone = {}; @@ -29,49 +29,44 @@ if (typeof cornerstoneTools === 'undefined') { clearTimeout(scrollTimeout); scrollTimeout = setTimeout(function() { - // !!!HACK/NOTE/WARNING!!! - // for some reason I am getting mousewheel and DOMMouseScroll events on my - // mac os x mavericks system when middle mouse button dragging. - // I couldn't find any info about this so this might break other systems - // webkit hack - if (e.originalEvent.type === 'mousewheel' && e.originalEvent.wheelDeltaY === 0) { - return; - } - // firefox hack - if (e.originalEvent.type === 'DOMMouseScroll' && e.originalEvent.axis === 1) { - return; - } + var element = e.target.parentNode; - var element = e.currentTarget; + if (!e.deltaY) { + return; + } var x; var y; - if (e.pageX !== undefined && e.pageY !== undefined) { x = e.pageX; y = e.pageY; - } else if (e.originalEvent && - e.originalEvent.pageX !== undefined && - e.originalEvent.pageY !== undefined) { - x = e.originalEvent.pageX; - y = e.originalEvent.pageY; - } else { - // IE9 & IE10 - x = e.x; - y = e.y; } var startingCoords = cornerstone.pageToPixel(element, x, y); - e = window.event || e; // old IE support - var wheelDelta = e.wheelDelta || -e.detail || -e.originalEvent.detail || -e.originalEvent.deltaY; - var direction = Math.max(-1, Math.min(1, (wheelDelta))); + var wheelDeltaPixels; + var pixelsPerLine = 40; + var pixelsPerPage = 800; + + if (e.deltaMode === 2) { + // DeltaY is in Pages + wheelDeltaPixels = e.deltaY * pixelsPerPage; + } else if (e.deltaMode === 1) { + // DeltaY is in Lines + wheelDeltaPixels = e.deltaY * pixelsPerLine; + } else { + // DeltaY is already in Pixels + wheelDeltaPixels = e.deltaY; + } + + var direction = e.deltaY < 0 ? -1 : 1; var mouseWheelData = { element: element, viewport: cornerstone.getViewport(element), image: cornerstone.getEnabledElement(element).image, direction: direction, + wheelDeltaPixels: wheelDeltaPixels, pageX: x, pageY: y, imageX: startingCoords.x, @@ -82,17 +77,15 @@ if (typeof cornerstoneTools === 'undefined') { }, scrollTimeoutDelay); } - var mouseWheelEvents = 'mousewheel DOMMouseScroll'; - function enable(element) { // Prevent handlers from being attached multiple times disable(element); - - $(element).on(mouseWheelEvents, mouseWheel); + + cornerstoneTools.addWheelListener(element, mouseWheel); } function disable(element) { - $(element).unbind(mouseWheelEvents, mouseWheel); + cornerstoneTools.removeWheelListener(element, mouseWheel); } // module exports @@ -370,7 +363,7 @@ if (typeof cornerstoneTools === 'undefined') { function enable(element) { // Prevent handlers from being attached multiple times disable(element); - + $(element).on('mousedown', mouseDown); $(element).on('mousemove', mouseMove); $(element).on('dblclick', mouseDoubleClick); @@ -407,7 +400,7 @@ if (typeof cornerstoneTools === 'undefined') { pressMaxDistance = 5, pageDistanceMoved, preventNextPinch = false; - + function onTouch(e) { console.log(e.type); var element = e.target.parentNode, @@ -488,7 +481,7 @@ if (typeof cornerstoneTools === 'undefined') { case 'pinchstart': isPress = false; clearTimeout(pressTimeout); - + lastScale = 1.0; break; @@ -681,7 +674,7 @@ if (typeof cornerstoneTools === 'undefined') { isPress = false; clearTimeout(pressTimeout); } - + eventType = 'CornerstoneToolsTouchDrag'; if (e.pointers.length > 1) { eventType = 'CornerstoneToolsMultiTouchDrag'; @@ -819,7 +812,7 @@ if (typeof cornerstoneTools === 'undefined') { var rotate = new Hammer.Rotate({ threshold: 0 }); - + // we want to detect both the same time pinch.recognizeWith(pan); pinch.recognizeWith(rotate); @@ -991,13 +984,13 @@ if (typeof cornerstoneTools === 'undefined') { if (eventData.which !== 0) { return; } - + // if we have no tool data for this element, do nothing var toolData = cornerstoneTools.getToolState(eventData.element, mouseToolInterface.toolType); if (!toolData) { return; } - + // We have tool data, search through all data // and see if we can activate a handle var imageNeedsUpdate = false; @@ -1050,7 +1043,7 @@ if (typeof cornerstoneTools === 'undefined') { var i; // now check to see if there is a handle we can move - + var preventHandleOutsideImage; if (mouseToolInterface.options && mouseToolInterface.options.preventHandleOutsideImage !== undefined) { preventHandleOutsideImage = mouseToolInterface.options.preventHandleOutsideImage; @@ -1230,12 +1223,12 @@ if (typeof cornerstoneTools === 'undefined') { (function($, cornerstone, cornerstoneMath, cornerstoneTools) { 'use strict'; - + function mouseButtonRectangleTool(mouseToolInterface, preventHandleOutsideImage) { ///////// BEGIN ACTIVE TOOL /////// function addNewMeasurement(mouseEventData) { var measurementData = mouseToolInterface.createNewMeasurement(mouseEventData); - + //prevent adding new measurement if tool returns nill if (!measurementData) { return; @@ -1243,7 +1236,7 @@ if (typeof cornerstoneTools === 'undefined') { // associate this data with this imageId so we can render it and manipulate it cornerstoneTools.addToolState(mouseEventData.element, mouseToolInterface.toolType, measurementData); - + // since we are dragging to another place to drop the end point, we can just activate // the end point and let the moveHandle move it for us. $(mouseEventData.element).off('CornerstoneToolsMouseMove', mouseMoveCallback); @@ -1274,13 +1267,13 @@ if (typeof cornerstoneTools === 'undefined') { if (eventData.which !== 0) { return; } - + // if we have no tool data for this element, do nothing var toolData = cornerstoneTools.getToolState(eventData.element, mouseToolInterface.toolType); if (toolData === undefined) { return; } - + // We have tool data, search through all data // and see if we can activate a handle var imageNeedsUpdate = false; @@ -1348,7 +1341,7 @@ if (typeof cornerstoneTools === 'undefined') { deleteIfHandleOutsideImage: true, preventHandleOutsideImage: preventHandleOutsideImage }; - + if (toolData !== undefined && mouseToolInterface.pointInsideRect !== undefined) { for (i = 0; i < toolData.data.length; i++) { data = toolData.data[i]; @@ -1447,6 +1440,8 @@ if (typeof cornerstoneTools === 'undefined') { 'use strict'; function mouseWheelTool(mouseWheelCallback) { + var configuration = {}; + var toolInterface = { activate: function(element) { $(element).off('CornerstoneToolsMouseWheel', mouseWheelCallback); @@ -1456,7 +1451,9 @@ if (typeof cornerstoneTools === 'undefined') { }, disable: function(element) {$(element).off('CornerstoneToolsMouseWheel', mouseWheelCallback);}, enable: function(element) {$(element).off('CornerstoneToolsMouseWheel', mouseWheelCallback);}, - deactivate: function(element) {$(element).off('CornerstoneToolsMouseWheel', mouseWheelCallback);} + deactivate: function(element) {$(element).off('CornerstoneToolsMouseWheel', mouseWheelCallback);}, + getConfiguration: function() { return configuration;}, + setConfiguration: function(config) {configuration = config;} }; return toolInterface; } @@ -1742,7 +1739,7 @@ if (typeof cornerstoneTools === 'undefined') { } // now check to see if there is a handle we can move - + // Average pixel width of index finger is 45-57 pixels // https://www.smashingmagazine.com/2012/02/finger-friendly-design-ideal-mobile-touchscreen-target-sizes/ var distance = 28; @@ -1801,7 +1798,7 @@ if (typeof cornerstoneTools === 'undefined') { if (touchToolInterface.doubleTapCallback) { $(element).off('CornerstoneToolsDoubleTap', touchToolInterface.doubleTapCallback); } - + if (touchToolInterface.pressCallback) { $(element).off('CornerstoneToolsTouchPress', touchToolInterface.pressCallback); } @@ -1973,7 +1970,7 @@ if (typeof cornerstoneTools === 'undefined') { start: cornerstone.pixelToCanvas(element, data.handles.start), end: cornerstone.pixelToCanvas(element, data.handles.end) }; - + var distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, coords); if (distanceToPoint < 5) { return true; @@ -1998,8 +1995,8 @@ if (typeof cornerstoneTools === 'undefined') { // we have tool data for this element - iterate over each one and draw it var context = eventData.canvasContext.canvas.getContext('2d'); context.setTransform(1, 0, 0, 1, 0, 0); - - //activation color + + //activation color var color; var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); var font = cornerstoneTools.textStyle.getFont(); @@ -2049,7 +2046,7 @@ if (typeof cornerstoneTools === 'undefined') { context.fillStyle = color; // Need to work on correct angle to measure. This is a cobb angle and we need to determine - // where lines cross to measure angle. For now it will show smallest angle. + // where lines cross to measure angle. For now it will show smallest angle. var dx1 = (Math.ceil(data.handles.start.x) - Math.ceil(data.handles.end.x)) * eventData.image.columnPixelSpacing; var dy1 = (Math.ceil(data.handles.start.y) - Math.ceil(data.handles.end.y)) * eventData.image.rowPixelSpacing; var dx2 = (Math.ceil(data.handles.start2.x) - Math.ceil(data.handles.end2.x)) * eventData.image.columnPixelSpacing; @@ -2079,7 +2076,7 @@ if (typeof cornerstoneTools === 'undefined') { pointNearTool: pointNearTool, toolType: toolType }); - + cornerstoneTools.angleTouch = cornerstoneTools.touchTool({ createNewMeasurement: createNewMeasurement, onImageRendered: onImageRendered, @@ -2125,7 +2122,7 @@ if (typeof cornerstoneTools === 'undefined') { var eventData = { mouseButtonMask: mouseEventData.which, }; - + function doneChangingTextCallback(text) { if (text !== null) { measurementData.text = text; @@ -2141,10 +2138,10 @@ if (typeof cornerstoneTools === 'undefined') { $(mouseEventData.element).on('CornerstoneToolsMouseDownActivate', eventData, cornerstoneTools.arrowAnnotate.mouseDownActivateCallback); $(mouseEventData.element).on('CornerstoneToolsMouseDoubleClick', eventData, cornerstoneTools.arrowAnnotate.mouseDoubleClickCallback); } - + // associate this data with this imageId so we can render it and manipulate it cornerstoneTools.addToolState(mouseEventData.element, toolType, measurementData); - + // since we are dragging to another place to drop the end point, we can just activate // the end point and let the moveHandle move it for us. $(mouseEventData.element).off('CornerstoneToolsMouseMove', cornerstoneTools.arrowAnnotate.mouseMoveCallback); @@ -2246,7 +2243,7 @@ if (typeof cornerstoneTools === 'undefined') { } else { color = cornerstoneTools.toolColors.getToolColor(); } - + // Draw the arrow var handleStartCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.start); var handleEndCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.end); @@ -2287,7 +2284,7 @@ if (typeof cornerstoneTools === 'undefined') { y: handleEndCanvas.y - textHeight / 2 }; } else { - // If the arrow is at the End position, the text should + // If the arrow is at the End position, the text should // be placed near the Start position textCoords = { x: handleStartCanvas.x - textWidth / 2 - distance, @@ -2302,7 +2299,7 @@ if (typeof cornerstoneTools === 'undefined') { data.handles.textBox.x = coords.x; data.handles.textBox.y = coords.y; } - + textCoords = cornerstone.pixelToCanvas(eventData.element, data.handles.textBox); var boundingBox = cornerstoneTools.drawTextBox(context, data.text, textCoords.x, textCoords.y, color); @@ -2319,7 +2316,7 @@ if (typeof cornerstoneTools === 'undefined') { x: (handleStartCanvas.x + handleEndCanvas.x) / 2, y: (handleStartCanvas.y + handleEndCanvas.y) / 2, }; - + var points = [ handleStartCanvas, handleEndCanvas, midpointCanvas ]; link.end.x = textCoords.x; @@ -2441,7 +2438,7 @@ if (typeof cornerstoneTools === 'undefined') { cornerstone.updateImage(element); // Allow relabelling via a callback config.changeTextCallback(data, eventData, doneChangingTextCallback); - + e.stopImmediatePropagation(); return false; } @@ -2486,10 +2483,10 @@ if (typeof cornerstoneTools === 'undefined') { $(element).off('CornerstoneToolsTouchStart', cornerstoneTools.arrowAnnotateTouch.touchStartCallback); $(element).off('CornerstoneToolsTouchStartActive', cornerstoneTools.arrowAnnotateTouch.touchDownActivateCallback); $(element).off('CornerstoneToolsTap', cornerstoneTools.arrowAnnotateTouch.tapCallback); - + // Allow relabelling via a callback config.changeTextCallback(eventData.handlePressed, eventData, doneChangingTextCallback); - + e.stopImmediatePropagation(); return false; } @@ -2504,10 +2501,10 @@ if (typeof cornerstoneTools === 'undefined') { $(element).off('CornerstoneToolsTouchStart', cornerstoneTools.arrowAnnotateTouch.touchStartCallback); $(element).off('CornerstoneToolsTouchStartActive', cornerstoneTools.arrowAnnotateTouch.touchDownActivateCallback); $(element).off('CornerstoneToolsTap', cornerstoneTools.arrowAnnotateTouch.tapCallback); - + // Allow relabelling via a callback config.changeTextCallback(data, eventData, doneChangingTextCallback); - + e.stopImmediatePropagation(); return false; } @@ -2549,7 +2546,7 @@ if (typeof cornerstoneTools === 'undefined') { function chooseLocation(e, eventData) { e.stopImmediatePropagation(); // Prevent CornerstoneToolsTouchStartActive from killing any press events - + // if we have no toolData for this element, return immediately as there is nothing to do var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); if (!toolData) { @@ -2665,7 +2662,7 @@ if (typeof cornerstoneTools === 'undefined') { var eventData = { mouseButtonMask: mouseButtonMask, }; - + // Clear any currently existing toolData var toolData = cornerstoneTools.getToolState(element, toolType); toolData = []; @@ -2880,7 +2877,7 @@ if (typeof cornerstoneTools === 'undefined') { y: eventData.currentPoints.image.y - 3 }; var textCoords = cornerstone.pixelToCanvas(eventData.element, coords); - + context.font = font; context.fillStyle = color; @@ -2902,7 +2899,7 @@ if (typeof cornerstoneTools === 'undefined') { var color = cornerstoneTools.toolColors.getActiveColor(); var font = cornerstoneTools.textStyle.getFont(); var config = cornerstoneTools.dragProbe.getConfiguration(); - + context.save(); if (config && config.shadow) { @@ -2927,7 +2924,7 @@ if (typeof cornerstoneTools === 'undefined') { toolCoords.x >= image.columns || toolCoords.y >= image.rows) { return; } - + if (image.color) { storedPixels = cornerstoneTools.getRGBPixels(element, toolCoords.x, toolCoords.y, 1, 1); text = 'R: ' + storedPixels[0] + ' G: ' + storedPixels[1] + ' B: ' + storedPixels[2]; @@ -2940,9 +2937,9 @@ if (typeof cornerstoneTools === 'undefined') { var modalityTag = 'x00080060'; var modality; if (eventData.image.data) { - modality = eventData.image.data.string(modalityTag); + modality = eventData.image.data.string(modalityTag); } - + if (modality === 'CT') { text += 'HU: '; } @@ -3008,7 +3005,7 @@ if (typeof cornerstoneTools === 'undefined') { } cornerstoneTools.dragProbe = cornerstoneTools.simpleMouseButtonTool(mouseDownCallback); - + cornerstoneTools.dragProbe.strategies = { default: defaultStrategy, minimal: minimalStrategy @@ -3288,7 +3285,7 @@ if (typeof cornerstoneTools === 'undefined') { meanStdDev = calculateMeanStdDev(pixels, ellipse); if (modality === 'PT') { - // If the image is from a PET scan, use the DICOM tags to + // If the image is from a PET scan, use the DICOM tags to // calculate the SUV from the mean and standard deviation. // Note that because we are using modality pixel values from getPixels, and @@ -3323,14 +3320,6 @@ if (typeof cornerstoneTools === 'undefined') { // Set the invalidated flag to false so that this data won't automatically be recalculated data.invalidated = false; - - var eventType = 'CornerstoneToolsMeasurementModified'; - var modifiedEventData = { - toolType: toolType, - element: element, - measurementData: data - }; - $(element).trigger(eventType, modifiedEventData); } // Define an array to store the rows of text for the textbox @@ -3379,7 +3368,7 @@ if (typeof cornerstoneTools === 'undefined') { } // If the textbox has not been moved by the user, it should be displayed on the right-most - // side of the tool. + // side of the tool. if (!data.handles.textBox.hasMoved) { // Find the rightmost side of the ellipse at its vertical center, and place the textbox here // Note that this calculates it in image coordinates @@ -3831,7 +3820,7 @@ if (typeof cornerstoneTools === 'undefined') { var mouseLocationCanvas = cornerstone.pixelToCanvas(eventData.element, config.mouseLocation.handles.start); if (j === (data.handles.length - 1)) { if (data.active && !config.freehand && !config.modifying) { - // If it's still being actively drawn, keep the last line to + // If it's still being actively drawn, keep the last line to // the mouse location context.lineTo(mouseLocationCanvas.x, mouseLocationCanvas.y); context.stroke(); @@ -3839,12 +3828,12 @@ if (typeof cornerstoneTools === 'undefined') { } } } - + // If the tool is active, draw a handle at the cursor location var options = { fill: fillColor }; - + if (data.active){ cornerstoneTools.drawHandles(context, eventData, config.mouseLocation.handles, color, options); } @@ -3939,7 +3928,7 @@ if (typeof cornerstoneTools === 'undefined') { if (existingToolData && existingToolData.data && existingToolData.data.length > 0) { return; } - + // create the measurement data for this tool with the end handle activated var measurementData = { visible: true, @@ -4060,7 +4049,7 @@ if (typeof cornerstoneTools === 'undefined') { // Strange fix, but restore doesn't seem to reset the line dashes? context.setLineDash([]); - + // draw the handles last, so they will be on top of the overlay cornerstoneTools.drawHandles(context, eventData, data.handles, color); context.restore(); @@ -4077,7 +4066,7 @@ if (typeof cornerstoneTools === 'undefined') { pointInsideRect: pointInsideRect, toolType: toolType }, preventHandleOutsideImage); - + cornerstoneTools.highlightTouch = cornerstoneTools.touchTool({ createNewMeasurement: createNewMeasurement, onImageRendered: onImageRendered, @@ -4224,7 +4213,7 @@ if (typeof cornerstoneTools === 'undefined') { var dx = (data.handles.end.x - data.handles.start.x) * (eventData.image.columnPixelSpacing || 1); var dy = (data.handles.end.y - data.handles.start.y) * (eventData.image.rowPixelSpacing || 1); - // Calculate the length, and create the text variable with the millimeters or pixels suffix + // Calculate the length, and create the text variable with the millimeters or pixels suffix var length = Math.sqrt(dx * dx + dy * dy); // Store the length inside the tool for outside access @@ -4244,7 +4233,7 @@ if (typeof cornerstoneTools === 'undefined') { x: Math.max(data.handles.start.x, data.handles.end.x), }; - // Depending on which handle has the largest x-value, + // Depending on which handle has the largest x-value, // set the y-value for the text box if (coords.x === data.handles.start.x) { coords.y = data.handles.start.y; @@ -4255,7 +4244,7 @@ if (typeof cornerstoneTools === 'undefined') { data.handles.textBox.x = coords.x; data.handles.textBox.y = coords.y; } - + var textCoords = cornerstone.pixelToCanvas(eventData.element, data.handles.textBox); // Move the textbox slightly to the right and upwards @@ -4358,11 +4347,14 @@ if (typeof cornerstoneTools === 'undefined') { var browserName; + var currentPoints; + /** Remove the magnifying glass when the mouse event ends */ function mouseUpCallback(e, eventData) { $(eventData.element).off('CornerstoneToolsMouseDrag', dragCallback); $(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback); $(eventData.element).off('CornerstoneToolsMouseClick', mouseUpCallback); + $(eventData.element).off('CornerstoneNewImage', newImageCallback); hideTool(eventData); } @@ -4378,19 +4370,30 @@ if (typeof cornerstoneTools === 'undefined') { $(eventData.element).on('CornerstoneToolsMouseDrag', eventData, dragCallback); $(eventData.element).on('CornerstoneToolsMouseUp', eventData, mouseUpCallback); $(eventData.element).on('CornerstoneToolsMouseClick', eventData, mouseUpCallback); + + currentPoints = eventData.currentPoints; + $(eventData.element).on('CornerstoneNewImage', eventData, newImageCallback); drawMagnificationTool(eventData); return false; // false = causes jquery to preventDefault() and stopPropagation() this event } } + function newImageCallback(e, eventData) { + eventData.currentPoints = currentPoints; + drawMagnificationTool(eventData); + } + function dragEndCallback(e, eventData) { $(eventData.element).off('CornerstoneToolsDragEnd', dragEndCallback); $(eventData.element).off('CornerstoneToolsTouchEnd', dragEndCallback); + $(eventData.element).off('CornerstoneNewImage', newImageCallback); hideTool(eventData); } /** Drag callback is triggered by both the touch and mouse magnify tools */ function dragCallback(e, eventData) { + currentPoints = eventData.currentPoints; + drawMagnificationTool(eventData); if (eventData.isTouchEvent === true) { $(eventData.element).on('CornerstoneToolsDragEnd', dragEndCallback); @@ -4443,7 +4446,7 @@ if (typeof cornerstoneTools === 'undefined') { // Fill it with the pixels that the mouse is clicking on zoomCtx.fillRect(0, 0, magnifySize, magnifySize); - + var copyFrom = { x: canvasLocation.x - 0.5 * getSize, y: canvasLocation.y - 0.5 * getSize @@ -4742,7 +4745,7 @@ if (typeof cornerstoneTools === 'undefined') { return false; // false = causes jquery to preventDefault() and stopPropagation() this event } } - + function dragCallback(e, eventData) { eventData.viewport.translation.x += (eventData.deltaPoints.page.x / eventData.viewport.scale); eventData.viewport.translation.y += (eventData.deltaPoints.page.y / eventData.viewport.scale); @@ -4836,7 +4839,7 @@ if (typeof cornerstoneTools === 'undefined') { context.save(); var data = toolData.data[i]; - + if (data.active) { color = cornerstoneTools.toolColors.getActiveColor(); } else { @@ -4881,7 +4884,7 @@ if (typeof cornerstoneTools === 'undefined') { y: data.handles.end.y - 3 }; var textCoords = cornerstone.pixelToCanvas(eventData.element, coords); - + context.font = font; context.fillStyle = color; @@ -5009,25 +5012,25 @@ if (typeof cornerstoneTools === 'undefined') { var context = eventData.canvasContext.canvas.getContext('2d'); context.setTransform(1, 0, 0, 1, 0, 0); - //activation color + //activation color var color; var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); var font = cornerstoneTools.textStyle.getFont(); var fontHeight = cornerstoneTools.textStyle.getFontSize(); var config = cornerstoneTools.rectangleRoi.getConfiguration(); - + for (var i = 0; i < toolData.data.length; i++) { context.save(); var data = toolData.data[i]; - + // Apply any shadow settings defined in the tool configuration if (config && config.shadow) { context.shadowColor = config.shadowColor || '#000000'; context.shadowOffsetX = config.shadowOffsetX || 1; context.shadowOffsetY = config.shadowOffsetY || 1; } - + //differentiate the color of activation tool if (data.active) { color = cornerstoneTools.toolColors.getActiveColor(); @@ -5179,7 +5182,7 @@ if (typeof cornerstoneTools === 'undefined') { horizontal: horizontalStrategy, vertical: verticalStrategy }; - + cornerstoneTools.rotate.strategy = defaultStrategy; cornerstoneTools.rotateTouchDrag = cornerstoneTools.touchDragTool(dragCallback); @@ -5257,6 +5260,424 @@ if (typeof cornerstoneTools === 'undefined') { // End Source; src/imageTools/saveImage.js +// Begin Source: src/imageTools/seedAnnotate.js +(function($, cornerstone, cornerstoneMath, cornerstoneTools) { + + 'use strict'; + + var toolType = 'seedAnnotate'; + + // Define a callback to get your text annotation + // This could be used, e.g. to open a modal + function getTextCallback(doneGetTextCallback) { + doneGetTextCallback(prompt('Enter your annotation:')); + } + + function changeTextCallback(data, eventData, doneChangingTextCallback) { + doneChangingTextCallback(prompt('Change your annotation:')); + } + + var configuration = { + getTextCallback: getTextCallback, + changeTextCallback: changeTextCallback, + drawHandles: false, + drawHandlesOnHover: true, + currentLetter: 'A', + currentNumber: 0, + showCoordinates: true, + countUp: true + }; + /// --- Mouse Tool --- /// + + ///////// BEGIN ACTIVE TOOL /////// + function addNewMeasurement(mouseEventData) { + var element = mouseEventData.element; + var config = cornerstoneTools.seedAnnotate.getConfiguration(); + var measurementData = createNewMeasurement(mouseEventData); + + function doneGetTextCallback(text) { + if (text !== null) { + measurementData.text = text; + } else { + cornerstoneTools.removeToolState(element, toolType, measurementData); + } + + measurementData.active = false; + cornerstone.updateImage(element); + } + + // associate this data with this imageId so we can render it and manipulate it + cornerstoneTools.addToolState(element, toolType, measurementData); + + cornerstone.updateImage(element); + cornerstoneTools.moveHandle(mouseEventData, toolType, measurementData, measurementData.handles.end, function() { + if (cornerstoneTools.anyHandlesOutsideImage(mouseEventData, measurementData.handles)) { + // delete the measurement + cornerstoneTools.removeToolState(element, toolType, measurementData); + } + + if (measurementData.text === undefined) { + config.getTextCallback(doneGetTextCallback); + } + + cornerstone.updateImage(element); + }); + } + + function createNewMeasurement(mouseEventData) { + // create the measurement data for this tool with the end handle activated + var measurementData = { + visible: true, + active: true, + handles: { + end: { + x: mouseEventData.currentPoints.image.x, + y: mouseEventData.currentPoints.image.y, + highlight: true, + active: false + }, + textBox: { + active: false, + hasMoved: false, + movesIndependently: false, + drawnIndependently: true, + allowedOutsideImage: true, + hasBoundingBox: true + } + } + }; + return measurementData; + } + ///////// END ACTIVE TOOL /////// + + function pointNearTool(element, data, coords) { + if (!data.handles.end) { + return; + } + + var realCoords = cornerstone.pixelToCanvas(element, data.handles.end); + var distanceToPoint = cornerstoneMath.point.distance(realCoords, coords); + return (distanceToPoint < 25); + } + + ///////// BEGIN IMAGE RENDERING /////// + function onImageRendered(e, eventData) { + // if we have no toolData for this element, return immediately as there is nothing to do + var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType); + if (!toolData) { + return; + } + + var enabledElement = eventData.enabledElement; + + // we have tool data for this element - iterate over each one and draw it + var context = eventData.canvasContext.canvas.getContext('2d'); + context.setTransform(1, 0, 0, 1, 0, 0); + + // We need the canvas width + var canvasWidth = eventData.canvasContext.canvas.width; + + var color; + var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); + var font = cornerstoneTools.textStyle.getFont(); + var config = cornerstoneTools.seedAnnotate.getConfiguration(); + + for (var i = 0; i < toolData.data.length; i++) { + context.save(); + + if (config && config.shadow) { + context.shadowColor = config.shadowColor || '#000000'; + context.shadowOffsetX = config.shadowOffsetX || 1; + context.shadowOffsetY = config.shadowOffsetY || 1; + } + + var data = toolData.data[i]; + + if (data.active) { + color = cornerstoneTools.toolColors.getActiveColor(); + } else { + color = cornerstoneTools.toolColors.getToolColor(); + } + + // Draw + var handleCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.end); + + // Draw the circle always at the end of the handle + cornerstoneTools.drawCircle(context, handleCanvas, color, lineWidth); + + var handleOptions = { + drawHandlesIfActive: (config && config.drawHandlesOnHover) + }; + + if (config.drawHandles) { + cornerstoneTools.drawHandles(context, eventData, handleCanvas, color, handleOptions); + } + + // Draw the text + if (data.text && data.text !== '') { + context.font = font; + + var textPlusCoords = ''; + + if ( config.showCoordinates ) { + textPlusCoords = data.text + ' x: ' + Math.round(data.handles.end.x) + + ' y: ' + Math.round(data.handles.end.y); + } else { + textPlusCoords = data.text; + } + + // Calculate the text coordinates. + var textWidth = context.measureText(textPlusCoords).width + 10; + var textHeight = cornerstoneTools.textStyle.getFontSize() + 10; + + var distance = Math.max(textWidth, textHeight) / 2 + 5; + if (handleCanvas.x > (canvasWidth / 2)) { + distance = -distance; + } + + var textCoords; + if (!data.handles.textBox.hasMoved) { + textCoords = { + x: handleCanvas.x - textWidth / 2 + distance, + y: handleCanvas.y - textHeight / 2 + }; + + var transform = cornerstone.internal.getTransform(enabledElement); + transform.invert(); + + var coords = transform.transformPoint(textCoords.x, textCoords.y); + data.handles.textBox.x = coords.x; + data.handles.textBox.y = coords.y; + } + + textCoords = cornerstone.pixelToCanvas(eventData.element, data.handles.textBox); + + var boundingBox = cornerstoneTools.drawTextBox(context, textPlusCoords, textCoords.x, textCoords.y, color); + data.handles.textBox.boundingBox = boundingBox; + + if (data.handles.textBox.hasMoved) { + // Draw dashed link line between tool and text + var link = { + start: {}, + end: {} + }; + + link.end.x = textCoords.x; + link.end.y = textCoords.y; + + link.start = handleCanvas; + + var boundingBoxPoints = [ + { + // Top middle point of bounding box + x: boundingBox.left + boundingBox.width / 2, + y: boundingBox.top + }, { + // Left middle point of bounding box + x: boundingBox.left, + y: boundingBox.top + boundingBox.height / 2 + }, { + // Bottom middle point of bounding box + x: boundingBox.left + boundingBox.width / 2, + y: boundingBox.top + boundingBox.height + }, { + // Right middle point of bounding box + x: boundingBox.left + boundingBox.width, + y: boundingBox.top + boundingBox.height / 2 + }, + ]; + + link.end = cornerstoneMath.point.findClosestPoint(boundingBoxPoints, link.start); + + context.beginPath(); + context.strokeStyle = color; + context.lineWidth = lineWidth; + context.setLineDash([ 2, 3 ]); + context.moveTo(link.start.x, link.start.y); + context.lineTo(link.end.x, link.end.y); + context.stroke(); + } + } + + context.restore(); + } + } + // ---- Touch tool ---- + + ///////// BEGIN ACTIVE TOOL /////// + function addNewMeasurementTouch(touchEventData) { + var element = touchEventData.element; + var config = cornerstoneTools.seedAnnotate.getConfiguration(); + var measurementData = createNewMeasurement(touchEventData); + + function doneGetTextCallback(text) { + if (text !== null) { + measurementData.text = text; + } else { + cornerstoneTools.removeToolState(element, toolType, measurementData); + } + + measurementData.active = false; + cornerstone.updateImage(element); + } + + // associate this data with this imageId so we can render it and manipulate it + cornerstoneTools.addToolState(element, toolType, measurementData); + + cornerstone.updateImage(element); + cornerstoneTools.moveHandle(touchEventData, toolType, measurementData, measurementData.handles.end, function() { + if (cornerstoneTools.anyHandlesOutsideImage(touchEventData, measurementData.handles)) { + // delete the measurement + cornerstoneTools.removeToolState(element, toolType, measurementData); + } + + if (measurementData.text === undefined) { + config.getTextCallback(doneGetTextCallback); + } + + cornerstone.updateImage(element); + }); + } + + function doubleClickCallback(e, eventData) { + var element = eventData.element; + var data; + + function doneChangingTextCallback(data, updatedText, deleteTool) { + if (deleteTool === true) { + cornerstoneTools.removeToolState(element, toolType, data); + } else { + data.text = updatedText; + } + + data.active = false; + cornerstone.updateImage(element); + } + + if (e.data && e.data.mouseButtonMask && !cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { + return false; + } + + var config = cornerstoneTools.seedAnnotate.getConfiguration(); + + var coords = eventData.currentPoints.canvas; + var toolData = cornerstoneTools.getToolState(element, toolType); + + // now check to see if there is a handle we can move + if (!toolData) { + return false; + } + + for (var i = 0; i < toolData.data.length; i++) { + data = toolData.data[i]; + if (pointNearTool(element, data, coords) || + cornerstoneTools.pointInsideBoundingBox(data.handles.textBox, coords)) { + + data.active = true; + cornerstone.updateImage(element); + // Allow relabelling via a callback + config.changeTextCallback(data, eventData, doneChangingTextCallback); + + e.stopImmediatePropagation(); + return false; + } + } + + return false; // false = causes jquery to preventDefault() and stopPropagation() this event + } + + function pressCallback(e, eventData) { + var element = eventData.element; + var data; + + function doneChangingTextCallback(data, updatedText, deleteTool) { + console.log('pressCallback doneChangingTextCallback'); + if (deleteTool === true) { + cornerstoneTools.removeToolState(element, toolType, data); + } else { + data.text = updatedText; + } + + data.active = false; + cornerstone.updateImage(element); + + $(element).on('CornerstoneToolsTouchStart', cornerstoneTools.seedAnnotateTouch.touchStartCallback); + $(element).on('CornerstoneToolsTouchStartActive', cornerstoneTools.seedAnnotateTouch.touchDownActivateCallback); + $(element).on('CornerstoneToolsTap', cornerstoneTools.seedAnnotateTouch.tapCallback); + } + + if (e.data && e.data.mouseButtonMask && !cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { + return false; + } + + var config = cornerstoneTools.seedAnnotate.getConfiguration(); + + var coords = eventData.currentPoints.canvas; + var toolData = cornerstoneTools.getToolState(element, toolType); + + // now check to see if there is a handle we can move + if (!toolData) { + return false; + } + + if (eventData.handlePressed) { + $(element).off('CornerstoneToolsTouchStart', cornerstoneTools.seedAnnotateTouch.touchStartCallback); + $(element).off('CornerstoneToolsTouchStartActive', cornerstoneTools.seedAnnotateTouch.touchDownActivateCallback); + $(element).off('CornerstoneToolsTap', cornerstoneTools.seedAnnotateTouch.tapCallback); + + // Allow relabelling via a callback + config.changeTextCallback(eventData.handlePressed, eventData, doneChangingTextCallback); + + e.stopImmediatePropagation(); + return false; + } + + for (var i = 0; i < toolData.data.length; i++) { + data = toolData.data[i]; + if (pointNearTool(element, data, coords) || + cornerstoneTools.pointInsideBoundingBox(data.handles.textBox, coords)) { + data.active = true; + cornerstone.updateImage(element); + + $(element).off('CornerstoneToolsTouchStart', cornerstoneTools.seedAnnotateTouch.touchStartCallback); + $(element).off('CornerstoneToolsTouchStartActive', cornerstoneTools.seedAnnotateTouch.touchDownActivateCallback); + $(element).off('CornerstoneToolsTap', cornerstoneTools.seedAnnotateTouch.tapCallback); + + // Allow relabelling via a callback + config.changeTextCallback(data, eventData, doneChangingTextCallback); + + e.stopImmediatePropagation(); + return false; + } + } + + return false; // false = causes jquery to preventDefault() and stopPropagation() this event + } + + cornerstoneTools.seedAnnotate = cornerstoneTools.mouseButtonTool({ + addNewMeasurement: addNewMeasurement, + createNewMeasurement: createNewMeasurement, + onImageRendered: onImageRendered, + pointNearTool: pointNearTool, + toolType: toolType, + mouseDoubleClickCallback: doubleClickCallback + }); + + cornerstoneTools.seedAnnotate.setConfiguration(configuration); + + cornerstoneTools.seedAnnotateTouch = cornerstoneTools.touchTool({ + addNewMeasurement: addNewMeasurementTouch, + createNewMeasurement: createNewMeasurement, + onImageRendered: onImageRendered, + pointNearTool: pointNearTool, + toolType: toolType, + pressCallback: pressCallback + }); + +})($, cornerstone, cornerstoneMath, cornerstoneTools); + +// End Source; src/imageTools/seedAnnotate.js + // Begin Source: src/imageTools/simpleAngle.js (function($, cornerstone, cornerstoneMath, cornerstoneTools) { @@ -5340,8 +5761,8 @@ if (typeof cornerstoneTools === 'undefined') { // we have tool data for this element - iterate over each one and draw it var context = eventData.canvasContext.canvas.getContext('2d'); context.setTransform(1, 0, 0, 1, 0, 0); - - //activation color + + //activation color var color; var lineWidth = cornerstoneTools.toolStyle.getToolWidth(); var font = cornerstoneTools.textStyle.getFont(); @@ -5424,7 +5845,7 @@ if (typeof cornerstoneTools === 'undefined') { if (rAngle) { var str = '00B0'; // degrees symbol var text = rAngle.toString() + String.fromCharCode(parseInt(str, 16)) + suffix; - + var distance = 15; var textCoords; @@ -5812,7 +6233,7 @@ if (typeof cornerstoneTools === 'undefined') { $(element).off('CornerstoneToolsMouseDoubleClick', cornerstoneTools.textMarker.mouseDoubleClickCallback); // Allow relabelling via a callback config.changeTextCallback(data, eventData, doneChangingTextCallback); - + e.stopImmediatePropagation(); return false; } @@ -5861,10 +6282,10 @@ if (typeof cornerstoneTools === 'undefined') { $(element).off('CornerstoneToolsTouchStart', cornerstoneTools.textMarkerTouch.touchStartCallback); $(element).off('CornerstoneToolsTap', cornerstoneTools.textMarkerTouch.tapCallback); $(element).off('CornerstoneToolsTouchPress', cornerstoneTools.textMarkerTouch.pressCallback); - + // Allow relabelling via a callback config.changeTextCallback(eventData.handlePressed, eventData, doneChangingTextCallback); - + e.stopImmediatePropagation(); return false; } @@ -5882,7 +6303,7 @@ if (typeof cornerstoneTools === 'undefined') { $(element).off('CornerstoneToolsTouchPress', cornerstoneTools.textMarkerTouch.pressCallback); // Allow relabelling via a callback config.changeTextCallback(data, eventData, doneChangingTextCallback); - + e.stopImmediatePropagation(); return false; } @@ -6006,6 +6427,8 @@ if (typeof cornerstoneTools === 'undefined') { minWindowWidth: 10 }; + var currentMouseButtonMask; + /** Calculates the minimum, maximum, and mean value in the given pixel array */ function calculateMinMaxMean(storedPixelLuminanceData, globalMin, globalMax) { var numPixels = storedPixelLuminanceData.length; @@ -6036,47 +6459,60 @@ if (typeof cornerstoneTools === 'undefined') { }; } - /* Applies the windowing procedure when the mouse drag ends */ - function dragEndCallback(e, eventData) { + /* Erases the toolData and rebinds the handlers when the image changes */ + function newImageCallback(e, eventData) { + //console.log('newImageCallback: ' + e.type); + var toolData = cornerstoneTools.getToolState(eventData.element, toolType); + if (toolData && toolData.data) { + toolData.data = []; + } + $(eventData.element).off('CornerstoneToolsMouseMove', dragCallback); $(eventData.element).off('CornerstoneToolsMouseDrag', dragCallback); + $(eventData.element).off('CornerstoneToolsMouseUp', dragEndCallback); $(eventData.element).off('CornerstoneToolsMouseClick', dragEndCallback); - - var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - if (!toolData) { - return; - } - - if (!toolData.data.length) { - return; - } - - // Update the endpoint as the mouse/touch is dragged - var endPoint = { - x: eventData.currentPoints.image.x, - y: eventData.currentPoints.image.y - }; - - toolData.data[0].endPoint = endPoint; - - applyWWWCRegion(eventData); var mouseData = { - mouseButtonMask: eventData.which + mouseButtonMask: currentMouseButtonMask }; $(eventData.element).on('CornerstoneToolsMouseDown', mouseData, mouseDownCallback); } - /** Calculates the minimum and maximum value in the given pixel array */ - function applyWWWCRegion(eventData) { + /* Applies the windowing procedure when the mouse drag ends */ + function dragEndCallback(e, eventData) { + //console.log('dragEndCallback: ' + e.type); + $(eventData.element).off('CornerstoneToolsMouseMove', dragCallback); + $(eventData.element).off('CornerstoneToolsMouseDrag', dragCallback); + + $(eventData.element).off('CornerstoneToolsMouseUp', dragEndCallback); + $(eventData.element).off('CornerstoneToolsMouseClick', dragEndCallback); + + var mouseData = { + mouseButtonMask: currentMouseButtonMask + }; + + $(eventData.element).on('CornerstoneToolsMouseDown', mouseData, mouseDownCallback); + var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - if (!toolData) { + if (!toolData || !toolData.data || !toolData.data.length) { return; } - if (!toolData.data.length) { + // Update the endpoint as the mouse/touch is dragged + toolData.data[0].endPoint = { + x: eventData.currentPoints.image.x, + y: eventData.currentPoints.image.y + }; + + applyWWWCRegion(eventData); + } + + /** Calculates the minimum and maximum value in the given pixel array */ + function applyWWWCRegion(eventData) { + var toolData = cornerstoneTools.getToolState(eventData.element, toolType); + if (!toolData || !toolData.data || !toolData.data.length) { return; } @@ -6122,6 +6558,7 @@ if (typeof cornerstoneTools === 'undefined') { } function whichMovement(e, eventData) { + //console.log('whichMovement: ' + e.type); var element = eventData.element; $(element).off('CornerstoneToolsMouseMove'); @@ -6138,12 +6575,12 @@ if (typeof cornerstoneTools === 'undefined') { /** Records the start point and attaches the drag event handler */ function mouseDownCallback(e, eventData) { + //console.log('mouseDownCallback: ' + e.type); if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) { $(eventData.element).on('CornerstoneToolsMouseDrag', eventData, whichMovement); $(eventData.element).on('CornerstoneToolsMouseMove', eventData, whichMovement); - $(eventData.element).on('CornerstoneToolsMouseUp', dragEndCallback); - $(eventData.element).off('CornerstoneToolsMouseDown'); + $(eventData.element).off('CornerstoneToolsMouseDown', mouseDownCallback); recordStartPoint(eventData); return false; } @@ -6168,13 +6605,10 @@ if (typeof cornerstoneTools === 'undefined') { /** Draws the rectangular region while the touch or mouse event drag occurs */ function dragCallback(e, eventData) { + //console.log('dragCallback: ' + e.type); // if we have no toolData for this element, return immediately as there is nothing to do var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - if (toolData === undefined) { - return; - } - - if (!toolData.data.length) { + if (!toolData || !toolData.data || !toolData.data.length) { return; } @@ -6190,11 +6624,7 @@ if (typeof cornerstoneTools === 'undefined') { function onImageRendered(e, eventData) { var toolData = cornerstoneTools.getToolState(eventData.element, toolType); - if (!toolData) { - return; - } - - if (!toolData.data.length) { + if (!toolData || !toolData.data || !toolData.data.length) { return; } @@ -6212,7 +6642,7 @@ if (typeof cornerstoneTools === 'undefined') { // Set to the active tool color var color = cornerstoneTools.toolColors.getActiveColor(); - + // Calculate the rectangle parameters var startPointCanvas = cornerstone.pixelToCanvas(eventData.element, startPoint); var endPointCanvas = cornerstone.pixelToCanvas(eventData.element, endPoint); @@ -6246,7 +6676,15 @@ if (typeof cornerstoneTools === 'undefined') { // --- Mouse tool enable / disable --- /// function disable(element) { $(element).off('CornerstoneToolsMouseDown', mouseDownCallback); + + $(element).off('CornerstoneToolsMouseUp', dragEndCallback); + $(element).off('CornerstoneToolsMouseClick', dragEndCallback); + + $(element).off('CornerstoneToolsMouseDrag', dragCallback); + $(element).off('CornerstoneToolsMouseMove', dragCallback); + $(element).off('CornerstoneImageRendered', onImageRendered); + $(element).off('CornerstoneNewImage', newImageCallback); cornerstone.updateImage(element); } @@ -6256,6 +6694,8 @@ if (typeof cornerstoneTools === 'undefined') { mouseButtonMask: mouseButtonMask, }; + currentMouseButtonMask = mouseButtonMask; + var toolData = cornerstoneTools.getToolState(element, toolType); if (!toolData) { var data = []; @@ -6263,12 +6703,23 @@ if (typeof cornerstoneTools === 'undefined') { } $(element).off('CornerstoneToolsMouseDown', mouseDownCallback); + $(element).off('CornerstoneToolsMouseUp', dragEndCallback); + $(element).off('CornerstoneToolsMouseClick', dragEndCallback); + $(element).off('CornerstoneToolsMouseDrag', dragCallback); + $(element).off('CornerstoneToolsMouseMove', dragCallback); + $(element).off('CornerstoneImageRendered', onImageRendered); + $(element).off('CornerstoneNewImage', newImageCallback); $(element).on('CornerstoneToolsMouseDown', eventData, mouseDownCallback); $(element).on('CornerstoneImageRendered', onImageRendered); + + // If the displayed image changes after the user has started clicking, we should + // cancel the handlers and prepare for another click + $(element).on('CornerstoneNewImage', newImageCallback); + cornerstone.updateImage(element); } @@ -6282,7 +6733,7 @@ if (typeof cornerstoneTools === 'undefined') { function activateTouchDrag(element) { var toolData = cornerstoneTools.getToolState(element, toolType); - if (toolData === undefined) { + if (!toolData) { var data = []; cornerstoneTools.addToolState(element, toolType, data); } @@ -6335,10 +6786,10 @@ if (typeof cornerstoneTools === 'undefined') { function changeViewportScale(viewport, ticks) { var config = cornerstoneTools.zoom.getConfiguration(); var pow = 1.7; - + var oldFactor = Math.log(viewport.scale) / Math.log(pow); var factor = oldFactor + ticks; - + var scale = Math.pow(pow, factor); if (config.maxScale && scale > config.maxScale) { viewport.scale = config.maxScale; @@ -6363,10 +6814,10 @@ if (typeof cornerstoneTools === 'undefined') { // Apply rotations if (viewport.rotation !== 0) { var angle = viewport.rotation * Math.PI / 180; - + var cosA = Math.cos(angle); var sinA = Math.sin(angle); - + var newX = shift.x * cosA - shift.y * sinA; var newY = shift.x * sinA + shift.y * cosA; @@ -6374,7 +6825,7 @@ if (typeof cornerstoneTools === 'undefined') { shift.y = newY; } - // Apply Flips + // Apply Flips if (viewport.hflip) { shift.x *= -1; } @@ -6396,7 +6847,7 @@ if (typeof cornerstoneTools === 'undefined') { // Now that the scale has been updated, determine the offset we need to apply to the center so we can // keep the original start location in the same position var newCoords = cornerstone.pageToPixel(element, eventData.startPoints.page.x, eventData.startPoints.page.y); - + // The shift we will use is the difference between the original image coordinates of the point we've selected // and the image coordinates of the same point on the page after the viewport scaling above has been performed // This shift is in image coordinates, and is designed to keep the target location fixed on the page. @@ -6407,7 +6858,7 @@ if (typeof cornerstoneTools === 'undefined') { // Correct the required shift using the viewport rotation and flip parameters shift = correctShift(shift, viewport); - + // Apply the shift to the Viewport's translation setting viewport.translation.x -= shift.x; viewport.translation.y -= shift.y; @@ -6511,6 +6962,14 @@ if (typeof cornerstoneTools === 'undefined') { cornerstone.setViewport(element, viewport); } + function zoomToCenterStrategy(eventData, ticks) { + var element = eventData.element; + + // Calculate the new scale factor based on how far the mouse has changed + var viewport = changeViewportScale(eventData.viewport, ticks); + cornerstone.setViewport(element, viewport); + } + function mouseUpCallback(e, eventData) { $(eventData.element).off('CornerstoneToolsMouseDrag', dragCallback); $(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback); @@ -6539,7 +6998,7 @@ if (typeof cornerstoneTools === 'undefined') { function mouseWheelCallback(e, eventData) { var ticks = -eventData.direction / 4; - + // Allow inversion of the mouse wheel scroll via a configuration option var config = cornerstoneTools.zoom.getConfiguration(); if (config && config.invert) { @@ -6582,7 +7041,8 @@ if (typeof cornerstoneTools === 'undefined') { cornerstoneTools.zoom = cornerstoneTools.simpleMouseButtonTool(mouseDownCallback); cornerstoneTools.zoom.strategies = { default: defaultStrategy, - translate: translateStrategy + translate: translateStrategy, + zoomToCenter: zoomToCenterStrategy }; cornerstoneTools.zoom.strategy = defaultStrategy; @@ -6759,7 +7219,7 @@ if (typeof cornerstoneTools === 'undefined') { if (handle.allowedOutsideImage === true) { return; } - + if (cornerstoneMath.point.insideRect(handle, imageRect) === false) { handleOutsideImage = true; } @@ -6829,7 +7289,7 @@ if (typeof cornerstoneTools === 'undefined') { function getHandleNearImagePoint(element, handles, coords, distanceThreshold) { var nearbyHandle; - + if (!handles) { return; } @@ -6855,7 +7315,7 @@ if (typeof cornerstoneTools === 'undefined') { } } }); - + return nearbyHandle; } @@ -6933,7 +7393,7 @@ if (typeof cornerstoneTools === 'undefined') { handle.x += eventData.deltaPoints.image.x; handle.y += eventData.deltaPoints.image.y; - + if (options.preventHandleOutsideImage === true) { handle.x = Math.max(handle.x, 0); handle.x = Math.min(handle.x, eventData.image.width); @@ -7006,7 +7466,7 @@ if (typeof cornerstoneTools === 'undefined') { if (handle.hasMoved === false) { handle.hasMoved = true; } - + handle.active = true; handle.x = eventData.currentPoints.image.x + distanceFromTool.x; handle.y = eventData.currentPoints.image.y + distanceFromTool.y; @@ -7067,7 +7527,7 @@ if (typeof cornerstoneTools === 'undefined') { handle.active = true; handle.x = eventData.currentPoints.image.x; handle.y = eventData.currentPoints.image.y; - + if (preventHandleOutsideImage) { handle.x = Math.max(handle.x, 0); handle.x = Math.min(handle.x, eventData.image.width); @@ -7093,7 +7553,7 @@ if (typeof cornerstoneTools === 'undefined') { $(element).on('CornerstoneToolsMouseMove', moveCallback); $(element).on('CornerstoneToolsMouseDrag', moveCallback); - + $(element).on('CornerstoneToolsMouseClick', moveEndCallback); if (e.type === 'CornerstoneToolsMouseDrag') { $(element).on('CornerstoneToolsMouseUp', moveEndCallback); @@ -7124,7 +7584,7 @@ if (typeof cornerstoneTools === 'undefined') { $(element).on('CornerstoneToolsMouseMove', whichMovement); $(element).on('CornerstoneToolsMeasurementRemoved', measurementRemovedCallback); $(element).on('CornerstoneToolsToolDeactivated', toolDeactivatedCallback); - + function moveEndCallback() { $(element).off('CornerstoneToolsMouseMove', moveCallback); $(element).off('CornerstoneToolsMouseDrag', moveCallback); @@ -7169,7 +7629,7 @@ if (typeof cornerstoneTools === 'undefined') { function moveCallback(e, eventData) { handle.x = eventData.currentPoints.image.x + distanceFromTouch.x; handle.y = eventData.currentPoints.image.y + distanceFromTouch.y; - + if (preventHandleOutsideImage) { handle.x = Math.max(handle.x, 0); handle.x = Math.min(handle.x, eventData.image.width); @@ -7188,7 +7648,7 @@ if (typeof cornerstoneTools === 'undefined') { }; $(element).trigger(eventType, modifiedEventData); } - + function moveEndCallback(e, eventData) { $(element).off('CornerstoneToolsTouchDrag', moveCallback); $(element).off('CornerstoneToolsTouchPinch', moveEndCallback); @@ -7208,7 +7668,7 @@ if (typeof cornerstoneTools === 'undefined') { data.active = false; handle.x = eventData.currentPoints.image.x + distanceFromTouch.x; handle.y = eventData.currentPoints.image.y + distanceFromTouch.y; - + if (preventHandleOutsideImage) { handle.x = Math.max(handle.x, 0); handle.x = Math.min(handle.x, eventData.image.width); @@ -7225,7 +7685,7 @@ if (typeof cornerstoneTools === 'undefined') { } function stopImmediatePropagation(e) { - // Stop the CornerstoneToolsTouchStart event from + // Stop the CornerstoneToolsTouchStart event from // become a CornerstoneToolsTouchStartActive event when // moveNewHandleTouch ends e.stopImmediatePropagation(); @@ -7250,7 +7710,7 @@ if (typeof cornerstoneTools === 'undefined') { data.active = false; handle.x = eventData.currentPoints.image.x + distanceFromTouch.x; handle.y = eventData.currentPoints.image.y + distanceFromTouch.y; - + if (preventHandleOutsideImage) { handle.x = Math.max(handle.x, 0); handle.x = Math.min(handle.x, eventData.image.width); @@ -7284,13 +7744,13 @@ if (typeof cornerstoneTools === 'undefined') { function touchDragCallback(e, eventData) { //console.log('touchMoveAllHandles touchDragCallback'); data.active = true; - + Object.keys(data.handles).forEach(function(name) { var handle = data.handles[name]; if (handle.movesIndependently === true) { return; } - + handle.x += eventData.deltaPoints.image.x; handle.y += eventData.deltaPoints.image.y; }); @@ -7426,7 +7886,7 @@ if (typeof cornerstoneTools === 'undefined') { var targetLocation = cornerstone.pageToPixel(element, aboveFinger.x, aboveFinger.y); function touchDragCallback(e, eventData) { - console.log('touchMoveHandle touchDragCallback: ' + e.type); + //console.log('touchMoveHandle touchDragCallback: ' + e.type); runAnimation.value = false; if (handle.hasMoved === false) { @@ -7440,7 +7900,7 @@ if (typeof cornerstoneTools === 'undefined') { x: currentPoints.page.x, y: currentPoints.page.y + fingerDistance }; - + targetLocation = cornerstone.pageToPixel(element, aboveFinger.x, aboveFinger.y); handle.x = targetLocation.x; handle.y = targetLocation.y; @@ -7459,7 +7919,7 @@ if (typeof cornerstoneTools === 'undefined') { $(element).on('CornerstoneToolsTouchDrag', touchDragCallback); function touchEndCallback(e, eventData) { - console.log('touchMoveHandle touchEndCallback: ' + e.type); + //console.log('touchMoveHandle touchEndCallback: ' + e.type); runAnimation.value = false; handle.active = false; @@ -7481,7 +7941,7 @@ if (typeof cornerstoneTools === 'undefined') { } $(element).on(touchEndEvents, touchEndCallback); - + animate(time, handle, runAnimation, enabledElement, targetLocation); } @@ -7856,7 +8316,7 @@ if (typeof cornerstoneTools === 'undefined') { thumbnail: 6, prefetch: 5 }; - + var lastElementInteracted; var awake = false; var grabDelay = 20; @@ -7937,7 +8397,7 @@ if (typeof cornerstoneTools === 'undefined') { var imageId = requestDetails.imageId; var doneCallback = requestDetails.doneCallback; var failCallback = requestDetails.failCallback; - + // Check if we already have this image promise in the cache var imagePromise = cornerstone.imageCache.getImagePromise(imageId); if (imagePromise) { @@ -7986,7 +8446,7 @@ if (typeof cornerstoneTools === 'undefined') { } var maxSimultaneousRequests = cornerstoneTools.getMaxSimultaneousRequests(); - + maxNumRequests = { interaction: Math.max(maxSimultaneousRequests, 1), thumbnail: Math.max(maxSimultaneousRequests - 2, 1), @@ -8075,7 +8535,7 @@ if (typeof cornerstoneTools === 'undefined') { var playClipToolData = cornerstoneTools.getToolState(element, toolType); var playClipData; - + if (!playClipToolData || !playClipToolData.data || !playClipToolData.data.length) { playClipData = { intervalId: undefined, @@ -8175,7 +8635,7 @@ if (typeof cornerstoneTools === 'undefined') { } var playClipData = playClipToolData.data[0]; - + clearInterval(playClipData.intervalId); playClipData.intervalId = undefined; } @@ -8296,7 +8756,7 @@ Display scroll progress bar across bottom of image. function nearestIndex(arr, x) { // Return index of nearest values in array - // http://stackoverflow.com/questions/25854212/return-index-of-nearest-values-in-an-array + // http://stackoverflow.com/questions/25854212/return-index-of-nearest-values-in-an-array var l = [], h = []; @@ -8307,7 +8767,7 @@ Display scroll progress bar across bottom of image. h.push(v); } }); - + return { low: arr.indexOf(max(l)), high: arr.indexOf(min(h)) @@ -8342,14 +8802,14 @@ Display scroll progress bar across bottom of image. } // Remove an imageIdIndex from the list of indices to request - // This fires when the individual image loading deferred is resolved + // This fires when the individual image loading deferred is resolved function removeFromList(imageIdIndex) { var index = stackPrefetch.indicesToRequest.indexOf(imageIdIndex); if (index > -1) { // don't remove last element if imageIdIndex not found stackPrefetch.indicesToRequest.splice(index, 1); } } - + // Remove all already cached images from the // indicesToRequest array stackPrefetchData.data[0].indicesToRequest.sort(sortNumber); @@ -8378,7 +8838,7 @@ Display scroll progress bar across bottom of image. var requestPoolManager = cornerstoneTools.requestPoolManager; requestPoolManager.clearRequestStack(requestType); - // Identify the nearest imageIdIndex to the currentImageIdIndex + // Identify the nearest imageIdIndex to the currentImageIdIndex var nearest = nearestIndex(stackPrefetch.indicesToRequest, stack.currentImageIdIndex); var imageId, @@ -8458,7 +8918,7 @@ Display scroll progress bar across bottom of image. if (imageIdIndex < 0) { return; } - + var stackPrefetchData = cornerstoneTools.getToolState(element, toolType); if (!stackPrefetchData || !stackPrefetchData.data || !stackPrefetchData.data.length) { return; @@ -8587,7 +9047,9 @@ Display scroll progress bar across bottom of image. } function mouseWheelCallback(e, eventData) { - var images = -eventData.direction; + var config = cornerstoneTools.stackScroll.getConfiguration(); + var pixelsPerImage = config.wheelDeltaPixelsPerImage || 100; + var images = eventData.direction * Math.max(1, Math.round(Math.abs(eventData.wheelDeltaPixels) / pixelsPerImage)); cornerstoneTools.scroll(eventData.element, images); } @@ -8598,7 +9060,7 @@ Display scroll progress bar across bottom of image. if (!toolData || !toolData.data || !toolData.data.length) { return; } - + var stackData = toolData.data[0]; var config = cornerstoneTools.stackScroll.getConfiguration(); @@ -8626,6 +9088,19 @@ Display scroll progress bar across bottom of image. cornerstoneTools.stackScroll = cornerstoneTools.simpleMouseButtonTool(mouseDownCallback); cornerstoneTools.stackScrollWheel = cornerstoneTools.mouseWheelTool(mouseWheelCallback); + var stackScrollWheelConfig = { + // Smaller numbers lead to faster scrolling + // 100 is the default here because in my empirical tests, + // a single tick of my mouse produces a value of 100 pixels + // on Firefox on Windows. This is the largest I noticed in my + // tests. In some cases (e.g. >500 image stacks), the user + // may want to speed up stack scrolling. Lowering this value + // can do this. + wheelDeltaPixelsPerImage: 100 + }; + + cornerstoneTools.stackScrollWheel.setConfiguration(stackScrollWheelConfig); + var options = { eventData: { deltaY: 0 @@ -8732,7 +9207,7 @@ Display scroll progress bar across bottom of image. if (!appState.elementToolState.hasOwnProperty(elementId)) { return; } - + var toolStateManager = cornerstoneTools.getElementToolStateManager(element); if (toolStateManager === cornerstoneTools.globalImageIdSpecificToolStateManager) { return; @@ -8851,7 +9326,7 @@ Display scroll progress bar across bottom of image. // a global frameOfReferenceSpecificToolStateManager - the most common case is to share 3d information // between stacks of images var globalFrameOfReferenceSpecificToolStateManager = newFrameOfReferenceSpecificToolStateManager(); - + // module/private exports cornerstoneTools.newFrameOfReferenceSpecificToolStateManager = newFrameOfReferenceSpecificToolStateManager; cornerstoneTools.globalFrameOfReferenceSpecificToolStateManager = globalFrameOfReferenceSpecificToolStateManager; @@ -8961,7 +9436,7 @@ Display scroll progress bar across bottom of image. // a global imageIdSpecificToolStateManager - the most common case is to share state between all // visible enabled images var globalImageIdSpecificToolStateManager = newImageIdSpecificToolStateManager(); - + // module/private exports cornerstoneTools.newImageIdSpecificToolStateManager = newImageIdSpecificToolStateManager; cornerstoneTools.globalImageIdSpecificToolStateManager = globalImageIdSpecificToolStateManager; @@ -8999,11 +9474,11 @@ Display scroll progress bar across bottom of image. function setErrorLoadingHandler(handler) { defaultErrorLoadingHandler = handler; } - + function getErrorLoadingHandler() { return defaultErrorLoadingHandler; } - + var loadHandlers = { setStartLoadHandler: setStartLoadHandler, getStartLoadHandler: getStartLoadHandler, @@ -9407,7 +9882,7 @@ Display scroll progress bar across bottom of image. function clearToolState(element, toolType) { var toolStateManager = getElementToolStateManager(element); var toolData = toolStateManager.get(element, toolType); - + // If any toolData actually exists, clear it if (toolData !== undefined) { toolData.data = []; @@ -9743,7 +10218,7 @@ Display scroll progress bar across bottom of image. 'use strict'; - // This function causes any scrolling actions within the stack to propagate to + // This function causes any scrolling actions within the stack to propagate to // all of the other viewports that are synced function stackScrollSynchronizer(synchronizer, sourceElement, targetElement, eventData) { // If the target and source are the same, stop @@ -9924,14 +10399,14 @@ Display scroll progress bar across bottom of image. } var sourceImageId = initialData.imageIds.sourceElements[sourceIndex]; - + var positionDifference; if (sourceImageId === targetImageId) { positionDifference = 0; } else { positionDifference = initialData.distances[sourceImageId][targetImageId]; } - + eventHandler(that, sourceElement, targetElement, eventData, positionDifference); }); ignoreFiredEvents = false; @@ -10539,7 +11014,7 @@ Display scroll progress bar across bottom of image. if (image.data === undefined) { return; } - + // image must be PET if (image.data.string('x00080060') !== 'PT') { return; @@ -10562,7 +11037,7 @@ Display scroll progress bar across bottom of image. var totalDose = petSequence.floatString('x00181074'); var halfLife = petSequence.floatString('x00181075'); var seriesAcquisitionTime = dicomParser.parseTM(image.data.string('x00080031')); - + if (!startTime || !totalDose || !halfLife || !seriesAcquisitionTime) { return; } @@ -10654,6 +11129,27 @@ Display scroll progress bar across bottom of image. // End Source; src/util/drawArrow.js +// Begin Source: src/util/drawCircle.js +(function(cornerstoneTools) { + + 'use strict'; + + function drawCircle(context, start, color, lineWidth) { + var handleRadius = 6; + context.beginPath(); + context.strokeStyle = color; + context.lineWidth = lineWidth; + context.arc(start.x, start.y, handleRadius, 0, 2 * Math.PI); + context.stroke(); + } + + // Module exports + cornerstoneTools.drawCircle = drawCircle; + +})(cornerstoneTools); + +// End Source; src/util/drawCircle.js + // Begin Source: src/util/drawEllipse.js (function(cornerstoneTools) { @@ -10856,7 +11352,7 @@ Display scroll progress bar across bottom of image. var ua = navigator.userAgent, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [], tem; - + if (/trident/i.test(M[1])) { tem = /\brv[ :]+(\d+)/g.exec(ua) || []; return 'IE ' + (tem[1] || ''); @@ -11014,9 +11510,9 @@ Display scroll progress bar across bottom of image. // Begin Source: src/util/pointInsideBoundingBox.js (function(cornerstoneMath, cornerstoneTools) { - + 'use strict'; - + function pointInsideBoundingBox(handle, coords) { if (!handle.boundingBox) { return; @@ -11212,6 +11708,14 @@ Display scroll progress bar across bottom of image. 'use strict'; + function isNumber(value) { + return typeof value === 'number' && !isNaN(value); + } + + function isInteger(number) { + return number % 1 === 0; + } + function scrollToIndex(element, newImageIdIndex) { var toolData = cornerstoneTools.getToolState(element, 'stack'); if (!toolData || !toolData.data || !toolData.data.length) { @@ -11220,6 +11724,12 @@ Display scroll progress bar across bottom of image. var stackData = toolData.data[0]; + if (!isNumber(newImageIdIndex)) { + throw 'scrollToIndex: index provided is not numeric: ' + newImageIdIndex; + } else if (isNumber(newImageIdIndex) && !isInteger(newImageIdIndex)) { + throw 'scrollToIndex: index provided is not an integer: ' + newImageIdIndex; + } + // Allow for negative indexing if (newImageIdIndex < 0) { newImageIdIndex += stackData.imageIds.length; @@ -11231,11 +11741,22 @@ Display scroll progress bar across bottom of image. var viewport = cornerstone.getViewport(element); function doneCallback(image) { - if (stackData.currentImageIdIndex === newImageIdIndex) { - cornerstone.displayImage(element, image, viewport); - if (endLoadingHandler) { - endLoadingHandler(element); - } + if (stackData.currentImageIdIndex !== newImageIdIndex) { + return; + } + + // Check if the element is still enabled in Cornerstone, + // if an error is thrown, stop here. + try { + // TODO: Add 'isElementEnabled' to Cornerstone? + cornerstone.getEnabledElement(element); + } catch(error) { + return; + } + + cornerstone.displayImage(element, image, viewport); + if (endLoadingHandler) { + endLoadingHandler(element); } } @@ -11273,7 +11794,7 @@ Display scroll progress bar across bottom of image. } var requestPoolManager = cornerstoneTools.requestPoolManager; - + var type = 'interaction'; requestPoolManager.clearRequestStack(type); @@ -11334,3 +11855,127 @@ Display scroll progress bar across bottom of image. })(cornerstone, cornerstoneTools); // End Source; src/util/setContextToDisplayFontSize.js + +// Begin Source: src/util/wheelListeners.js +(function(cornerstoneTools) { + + 'use strict'; + + // Thanks to Andrei Kashcha (@anvaka) + // https://github.com/anvaka/wheel/blob/master/index.js + + /** + * This module unifies handling of mouse whee event across different browsers + * + * See https://developer.mozilla.org/en-US/docs/Web/Reference/Events/wheel?redirectlocale=en-US&redirectslug=DOM%2FMozilla_event_reference%2Fwheel + * for more details + * + * Usage: + * var addWheelListener = require('wheel').addWheelListener; + * var removeWheelListener = require('wheel').removeWheelListener; + * addWheelListener(domElement, function (e) { + * // mouse wheel event + * }); + * removeWheelListener(domElement, function); + */ + // by default we shortcut to 'addEventListener': + + // creates a global "addWheelListener" method + // example: addWheelListener( elem, function( e ) { console.log( e.deltaY ); e.preventDefault(); } ); + + var prefix = '', + _addEventListener, + _removeEventListener, + support; + + function detectEventModel(window, document) { + if (window && window.addEventListener) { + _addEventListener = 'addEventListener'; + _removeEventListener = 'removeEventListener'; + } else { + _addEventListener = 'attachEvent'; + _removeEventListener = 'detachEvent'; + prefix = 'on'; + } + + if (document) { + // detect available wheel event + support = 'onwheel' in document.createElement('div') ? 'wheel' : // Modern browsers support "wheel" + document.onmousewheel !== undefined ? 'mousewheel' : // Webkit and IE support at least "mousewheel" + 'DOMMouseScroll'; // let's assume that remaining browsers are older Firefox + } else { + support = 'wheel'; + } + } + + function addWheelListener(elem, callback, useCapture) { + detectEventModel(window, document); + + _addWheelListener(elem, support, callback, useCapture); + + // handle MozMousePixelScroll in older Firefox + if (support === 'DOMMouseScroll') { + _addWheelListener(elem, 'MozMousePixelScroll', callback, useCapture); + } + } + + function removeWheelListener(elem, callback, useCapture) { + detectEventModel(window, document); + + _removeWheelListener(elem, support, callback, useCapture); + + // handle MozMousePixelScroll in older Firefox + if (support === 'DOMMouseScroll') { + _removeWheelListener(elem, 'MozMousePixelScroll', callback, useCapture); + } + } + + function _removeWheelListener(elem, eventName, callback, useCapture) { + elem[_removeEventListener](prefix + eventName, callback, useCapture || false); + } + + function _addWheelListener(elem, eventName, callback, useCapture) { + elem[_addEventListener](prefix + eventName, support === 'wheel' ? callback : function(originalEvent) { + if (!originalEvent) { + originalEvent = window.event; + } + + // create a normalized event object + var event = { + // keep a ref to the original event object + originalEvent: originalEvent, + target: originalEvent.target || originalEvent.srcElement, + type: 'wheel', + deltaMode: originalEvent.type === 'MozMousePixelScroll' ? 0 : 1, + deltaX: 0, + deltaY: 0, + deltaZ: 0, + preventDefault: function() { + return originalEvent.preventDefault ? originalEvent.preventDefault() : false; + } + }; + + // calculate deltaY (and deltaX) according to the event + if (support === 'mousewheel') { + event.deltaY = -1 / 40 * originalEvent.wheelDelta; + // Webkit also support wheelDeltaX + if (originalEvent.wheelDeltaX) { + event.deltaX = -1 / 40 * originalEvent.wheelDeltaX; + } + } else { + event.deltaY = originalEvent.detail; + } + + // it's time to fire the callback + return callback(event); + + }, useCapture || false); + } + + // Module exports + cornerstoneTools.addWheelListener = addWheelListener; + cornerstoneTools.removeWheelListener = removeWheelListener; + +})(cornerstoneTools); + +// End Source; src/util/wheelListeners.js diff --git a/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.html b/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.html index e2037ff1b..be3b47692 100644 --- a/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.html +++ b/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.html @@ -8,7 +8,7 @@
Please drag a stack here to view images.
- {{>loadingIndicator}} {{>viewportOverlay}} + {{>loadingIndicator}} {{>viewportOrientationMarkers}} diff --git a/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js b/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js index 68217db35..161538038 100644 --- a/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js +++ b/Packages/ohif-viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js @@ -144,7 +144,7 @@ function loadDisplaySetIntoViewport(data, templateData) { } // Caches element's jQuery object - const jQueryEl = $(element); + const $element = $(element); // Update the enabled element with the image and viewport data // This is not usually necessary, but we need them stored in case @@ -207,8 +207,8 @@ function loadDisplaySetIntoViewport(data, templateData) { // Hide the viewport instructions (i.e. 'Drag a stack here') and show // the viewport overlay data. - jQueryEl.siblings('.viewportInstructions').hide(); - jQueryEl.siblings('.imageViewerViewportOverlay').show(); + $element.siblings('.viewportInstructions').hide(); + $element.siblings('.imageViewerViewportOverlay').show(); // Add stack state managers for the stack tool, CINE tool, and reference lines cornerstoneTools.addStackStateManager(element, ['stack', 'playClip', 'referenceLines']); @@ -276,8 +276,8 @@ function loadDisplaySetIntoViewport(data, templateData) { }; // Attach the onImageRendered callback to the CornerstoneImageRendered event - jQueryEl.off('CornerstoneImageRendered', onImageRendered); - jQueryEl.on('CornerstoneImageRendered', onImageRendered); + $element.off('CornerstoneImageRendered', onImageRendered); + $element.on('CornerstoneImageRendered', onImageRendered); // Set a random value for the Session variable in order to trigger an overlay update Session.set('CornerstoneImageRendered' + viewportIndex, Random.id()); @@ -288,7 +288,7 @@ function loadDisplaySetIntoViewport(data, templateData) { OHIF.log.info('imageViewerViewport onNewImage'); // Update the metaData for missing fields - updateMetaData(eventData.enabledElement.image); + //updateMetaData(eventData.enabledElement.image); // Update the templateData with the new imageId // This allows the template helpers to update reactively @@ -315,12 +315,22 @@ function loadDisplaySetIntoViewport(data, templateData) { }; // Attach the onNewImage callback to the CornerstoneNewImage event - jQueryEl.off('CornerstoneNewImage', onNewImage); - jQueryEl.on('CornerstoneNewImage', onNewImage); + $element.off('CornerstoneNewImage', onNewImage); + $element.on('CornerstoneNewImage', onNewImage); // Set a random value for the Session variable in order to trigger an overlay update Session.set('CornerstoneNewImage' + viewportIndex, Random.id()); + function onStackScroll(e, eventData) { + // Update the imageSlider value + Session.set('CornerstoneNewImage' + viewportIndex, Random.id()); + } + + $element.off('CornerstoneStackScroll', onStackScroll); + if (stack.imageIds.length > 1) { + $element.on('CornerstoneStackScroll', onStackScroll); + } + // Define a function to trigger an event whenever a new viewport is being used // This is used to update the value of the "active viewport", when the user interacts // with a new viewport element @@ -352,8 +362,8 @@ function loadDisplaySetIntoViewport(data, templateData) { }; // Attach the sendActivationTrigger function to all of the Cornerstone interaction events - jQueryEl.off(allCornerstoneEvents, sendActivationTrigger); - jQueryEl.on(allCornerstoneEvents, sendActivationTrigger); + $element.off(allCornerstoneEvents, sendActivationTrigger); + $element.on(allCornerstoneEvents, sendActivationTrigger); ViewerData[contentId].loadedSeriesData = layoutManager.viewportData; diff --git a/Packages/ohif-viewerbase/client/components/viewer/loadingIndicator/loadingIndicator.js b/Packages/ohif-viewerbase/client/components/viewer/loadingIndicator/loadingIndicator.js index 913b72e34..94d407b12 100644 --- a/Packages/ohif-viewerbase/client/components/viewer/loadingIndicator/loadingIndicator.js +++ b/Packages/ohif-viewerbase/client/components/viewer/loadingIndicator/loadingIndicator.js @@ -11,6 +11,7 @@ var loadHandlerTimeout; startLoadingHandler = function(element) { clearTimeout(loadHandlerTimeout); loadHandlerTimeout = setTimeout(function() { + console.log('startLoading'); var elem = $(element); elem.siblings('.imageViewerErrorLoadingIndicator').css('display', 'none'); elem.find('canvas').not('.magnifyTool').addClass("faded"); diff --git a/Packages/ohif-viewerbase/lib/enableHotkeys.js b/Packages/ohif-viewerbase/lib/enableHotkeys.js index d7111698e..844fa960b 100644 --- a/Packages/ohif-viewerbase/lib/enableHotkeys.js +++ b/Packages/ohif-viewerbase/lib/enableHotkeys.js @@ -6,7 +6,7 @@ Meteor.startup(function() { OHIF.viewer = {}; } - OHIF.viewer.loadIndicatorDelay = 3000; + OHIF.viewer.loadIndicatorDelay = 200; OHIF.viewer.defaultTool = 'wwwc'; OHIF.viewer.refLinesEnabled = true; OHIF.viewer.isPlaying = {}; diff --git a/StandaloneViewer/StandaloneViewer/client/components/viewer/viewer.js b/StandaloneViewer/StandaloneViewer/client/components/viewer/viewer.js index 225c83f8e..cada1b3e5 100644 --- a/StandaloneViewer/StandaloneViewer/client/components/viewer/viewer.js +++ b/StandaloneViewer/StandaloneViewer/client/components/viewer/viewer.js @@ -1,14 +1,6 @@ import { OHIF } from 'meteor/ohif:core'; OHIF.viewer = OHIF.viewer || {}; -OHIF.viewer.loadIndicatorDelay = 500; -OHIF.viewer.defaultTool = 'wwwc'; -OHIF.viewer.refLinesEnabled = true; -OHIF.viewer.isPlaying = {}; -OHIF.viewer.cine = { - framesPerSecond: 24, - loop: true -}; OHIF.viewer.functionList = { toggleCineDialog: toggleCineDialog,