ohif-viewer/Packages/cornerstone/client/cornerstoneTools.js

9914 lines
363 KiB
JavaScript

/*! cornerstoneTools - v0.7.6 - 2015-10-08 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstoneTools */
// Begin Source: src/header.js
if (typeof cornerstone === 'undefined') {
cornerstone = {};
}
if (typeof cornerstoneTools === 'undefined') {
cornerstoneTools = {
referenceLines: {},
orientation: {}
};
}
// End Source; src/header.js
// Begin Source: src/inputSources/mouseWheelInput.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function mouseWheel(e) {
// !!!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.currentTarget;
var startingCoords = cornerstone.pageToPixel(element, e.pageX || e.originalEvent.pageX, e.pageY || e.originalEvent.pageY);
e = window.event || e; // old IE support
var wheelDelta = e.wheelDelta || -e.detail || -e.originalEvent.detail;
var direction = Math.max(-1, Math.min(1, (wheelDelta)));
var mouseWheelData = {
element: element, viewport: cornerstone.getViewport(element), image: cornerstone.getEnabledElement(element).image, direction: direction, pageX: e.pageX || e.originalEvent.pageX, pageY: e.pageY || e.originalEvent.pageY, imageX: startingCoords.x, imageY: startingCoords.y
};
$(element).trigger('CornerstoneToolsMouseWheel', mouseWheelData);
}
var mouseWheelEvents = 'mousewheel DOMMouseScroll';
function enable(element) {
$(element).on(mouseWheelEvents, mouseWheel);
}
function disable(element) {
$(element).unbind(mouseWheelEvents, mouseWheel);
}
// module exports
cornerstoneTools.mouseWheelInput = {
enable: enable, disable: disable
};
})($, cornerstone, cornerstoneTools);
// End Source; src/inputSources/mouseWheelInput.js
// Begin Source: src/inputSources/mouseInput.js
(function($, cornerstone, cornerstoneMath, cornerstoneTools) {
'use strict';
var isClickEvent;
var preventClickTimeout;
var clickDelay = 200;
function preventClickHandler() {
isClickEvent = false;
}
function activateMouseDown(mouseEventDetail) {
$(mouseEventDetail.element).trigger('CornerstoneToolsMouseDownActivate', mouseEventDetail);
}
function mouseDoubleClick(e) {
var element = e.currentTarget;
var eventType = 'CornerstoneToolsMouseDoubleClick';
var startPoints = {
page: cornerstoneMath.point.pageToPoint(e), image: cornerstone.pageToPixel(element, e.pageX, e.pageY), client: {
x: e.clientX, y: e.clientY
}
};
startPoints.canvas = cornerstone.pixelToCanvas(element, startPoints.image);
var lastPoints = cornerstoneTools.copyPoints(startPoints);
var eventData = {
event: e,
which: e.which,
viewport: cornerstone.getViewport(element),
image: cornerstone.getEnabledElement(element).image,
element: element,
startPoints: startPoints,
lastPoints: lastPoints,
currentPoints: startPoints,
deltaPoints: {
x: 0, y: 0
},
type: eventType
};
var event = $.Event(eventType, eventData);
$(eventData.element).trigger(event, eventData);
}
function mouseDown(e) {
preventClickTimeout = setTimeout(preventClickHandler, clickDelay);
var element = e.currentTarget;
var eventType = 'CornerstoneToolsMouseDown';
// Prevent CornerstoneToolsMouseMove while mouse is down
$(element).off('mousemove', mouseMove);
var startPoints = {
page: cornerstoneMath.point.pageToPoint(e), image: cornerstone.pageToPixel(element, e.pageX, e.pageY), client: {
x: e.clientX, y: e.clientY
}
};
startPoints.canvas = cornerstone.pixelToCanvas(element, startPoints.image);
var lastPoints = cornerstoneTools.copyPoints(startPoints);
var eventData = {
event: e,
which: e.which,
viewport: cornerstone.getViewport(element),
image: cornerstone.getEnabledElement(element).image,
element: element,
startPoints: startPoints,
lastPoints: lastPoints,
currentPoints: startPoints,
deltaPoints: {
x: 0, y: 0
},
type: eventType
};
var event = $.Event(eventType, eventData);
$(eventData.element).trigger(event, eventData);
if (event.isImmediatePropagationStopped() === false) {
// no tools responded to this event, give the active tool a chance
if (activateMouseDown(eventData) === true) {
return cornerstoneTools.pauseEvent(e);
}
}
var whichMouseButton = e.which;
function onMouseMove(e) {
// calculate our current points in page and image coordinates
var eventType = 'CornerstoneToolsMouseDrag';
var currentPoints = {
page: cornerstoneMath.point.pageToPoint(e), image: cornerstone.pageToPixel(element, e.pageX, e.pageY), client: {
x: e.clientX, y: e.clientY
}
};
currentPoints.canvas = cornerstone.pixelToCanvas(element, currentPoints.image);
// Calculate delta values in page and image coordinates
var deltaPoints = {
page: cornerstoneMath.point.subtract(currentPoints.page, lastPoints.page), image: cornerstoneMath.point.subtract(currentPoints.image, lastPoints.image), client: cornerstoneMath.point.subtract(currentPoints.client, lastPoints.client), canvas: cornerstoneMath.point.subtract(currentPoints.canvas, lastPoints.canvas)
};
var eventData = {
which: whichMouseButton,
viewport: cornerstone.getViewport(element),
image: cornerstone.getEnabledElement(element).image,
element: element,
startPoints: startPoints,
lastPoints: lastPoints,
currentPoints: currentPoints,
deltaPoints: deltaPoints,
type: eventType
};
$(eventData.element).trigger(eventType, eventData);
// update the last points
lastPoints = cornerstoneTools.copyPoints(currentPoints);
// prevent left click selection of DOM elements
return cornerstoneTools.pauseEvent(e);
}
// hook mouseup so we can unbind our event listeners
// when they stop dragging
function onMouseUp(e) {
// Cancel the timeout preventing the click event from triggering
clearTimeout(preventClickTimeout);
var eventType = 'CornerstoneToolsMouseUp';
if (isClickEvent) {
eventType = 'CornerstoneToolsMouseClick';
}
// calculate our current points in page and image coordinates
var currentPoints = {
page: cornerstoneMath.point.pageToPoint(e),
image: cornerstone.pageToPixel(element, e.pageX, e.pageY),
client: {
x: e.clientX,
y: e.clientY
}
};
currentPoints.canvas = cornerstone.pixelToCanvas(element, currentPoints.image);
// Calculate delta values in page and image coordinates
var deltaPoints = {
page: cornerstoneMath.point.subtract(currentPoints.page, lastPoints.page),
image: cornerstoneMath.point.subtract(currentPoints.image, lastPoints.image),
client: cornerstoneMath.point.subtract(currentPoints.client, lastPoints.client),
canvas: cornerstoneMath.point.subtract(currentPoints.canvas, lastPoints.canvas)
};
var eventData = {
event: e,
which: whichMouseButton,
viewport: cornerstone.getViewport(element),
image: cornerstone.getEnabledElement(element).image,
element: element, startPoints: startPoints,
lastPoints: lastPoints,
currentPoints: currentPoints,
deltaPoints: deltaPoints,
type: eventType
};
var event = $.Event(eventType, eventData);
$(eventData.element).trigger(event, eventData);
$(document).off('mousemove', onMouseMove);
$(document).off('mouseup', onMouseUp);
$(eventData.element).on('mousemove', mouseMove);
isClickEvent = true;
}
$(document).on('mousemove', onMouseMove);
$(document).on('mouseup', onMouseUp);
return cornerstoneTools.pauseEvent(e);
}
function mouseMove(e) {
var element = e.currentTarget;
var eventType = 'CornerstoneToolsMouseMove';
var startPoints = {
page: cornerstoneMath.point.pageToPoint(e), image: cornerstone.pageToPixel(element, e.pageX, e.pageY), client: {
x: e.clientX, y: e.clientY
}
};
startPoints.canvas = cornerstone.pixelToCanvas(element, startPoints.image);
var lastPoints = cornerstoneTools.copyPoints(startPoints);
var whichMouseButton = e.which;
// calculate our current points in page and image coordinates
var currentPoints = {
page: cornerstoneMath.point.pageToPoint(e), image: cornerstone.pageToPixel(element, e.pageX, e.pageY), client: {
x: e.clientX, y: e.clientY
}
};
currentPoints.canvas = cornerstone.pixelToCanvas(element, currentPoints.image);
// Calculate delta values in page and image coordinates
var deltaPoints = {
page: cornerstoneMath.point.subtract(currentPoints.page, lastPoints.page), image: cornerstoneMath.point.subtract(currentPoints.image, lastPoints.image), client: cornerstoneMath.point.subtract(currentPoints.client, lastPoints.client), canvas: cornerstoneMath.point.subtract(currentPoints.canvas, lastPoints.canvas)
};
var eventData = {
which: whichMouseButton,
viewport: cornerstone.getViewport(element),
image: cornerstone.getEnabledElement(element).image,
element: element,
startPoints: startPoints,
lastPoints: lastPoints,
currentPoints: currentPoints,
deltaPoints: deltaPoints,
type: eventType
};
$(element).trigger(eventType, eventData);
// update the last points
lastPoints = cornerstoneTools.copyPoints(currentPoints);
}
function disable(element) {
$(element).off('mousedown', mouseDown);
$(element).off('mousemove', mouseMove);
$(element).off('dblclick', mouseDoubleClick);
}
function enable(element) {
// Prevent handlers from being attached multiple times
disable(element);
$(element).on('mousedown', mouseDown);
$(element).on('mousemove', mouseMove);
$(element).on('dblclick', mouseDoubleClick);
}
// module exports
cornerstoneTools.mouseInput = {
enable: enable,
disable: disable
};
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/inputSources/mouseInput.js
// Begin Source: src/inputSources/touchInput.js
(function($, cornerstone, cornerstoneMath, cornerstoneTools) {
'use strict';
/*jshint newcap: false */
var initScale = 1.0,
lastRotation = 0.0,
startPoints,
currentPoints,
lastPoints,
deltaPoints,
eventData;
function onTouch(e) {
console.log(e.type);
var element = e.target.parentNode,
event,
eventType;
// Prevent mouse events from occurring alongside touch events
e.preventDefault();
switch (e.type) {
case 'tap':
// calculate our current points in page and image coordinates
currentPoints = {
page: cornerstoneMath.point.pageToPoint(e.pointers[0]),
image: cornerstone.pageToPixel(element, e.pointers[0].pageX, e.pointers[0].pageY),
client: {
x: e.pointers[0].clientX,
y: e.pointers[0].clientY
}
};
currentPoints.canvas = cornerstone.pixelToCanvas(element, currentPoints.image);
eventType = 'CornerstoneToolsTap';
eventData = {
event: e,
viewport: cornerstone.getViewport(element),
image: cornerstone.getEnabledElement(element).image,
element: element,
currentPoints: currentPoints,
type: eventType,
isTouchEvent: true
};
event = $.Event(eventType, eventData);
$(element).trigger(event, eventData);
break;
case 'doubletap':
// calculate our current points in page and image coordinates
currentPoints = {
page: cornerstoneMath.point.pageToPoint(e.pointers[0]),
image: cornerstone.pageToPixel(element, e.pointers[0].pageX, e.pointers[0].pageY),
client: {
x: e.pointers[0].clientX,
y: e.pointers[0].clientY
}
};
currentPoints.canvas = cornerstone.pixelToCanvas(element, currentPoints.image);
eventType = 'CornerstoneToolsDoubleTap';
eventData = {
event: e,
viewport: cornerstone.getViewport(element),
image: cornerstone.getEnabledElement(element).image,
element: element,
currentPoints: currentPoints,
type: eventType,
isTouchEvent: true
};
event = $.Event(eventType, eventData);
$(element).trigger(event, eventData);
break;
case 'press':
// calculate our current points in page and image coordinates
currentPoints = {
page: cornerstoneMath.point.pageToPoint(e.pointers[0]),
image: cornerstone.pageToPixel(element, e.pointers[0].pageX, e.pointers[0].pageY),
client: {
x: e.pointers[0].clientX,
y: e.pointers[0].clientY
}
};
currentPoints.canvas = cornerstone.pixelToCanvas(element, currentPoints.image);
eventType = 'CornerstoneToolsTouchPress';
eventData = {
event: e,
viewport: cornerstone.getViewport(element),
image: cornerstone.getEnabledElement(element).image,
element: element,
currentPoints: currentPoints,
type: eventType,
isTouchEvent: true
};
event = $.Event(eventType, eventData);
$(element).trigger(event, eventData);
break;
case 'pinchstart':
var viewport = cornerstone.getViewport(element);
initScale = viewport.scale || 1;
break;
case 'pinchmove':
var scaleChange = initScale * e.scale;
startPoints = {
page: e.center,
image: cornerstone.pageToPixel(element, e.center.x, e.center.y),
};
startPoints.canvas = cornerstone.pixelToCanvas(element, startPoints.image);
eventType = 'CornerstoneToolsTouchPinch';
eventData = {
event: e,
startPoints: startPoints,
viewport: cornerstone.getViewport(element),
image: cornerstone.getEnabledElement(element).image,
element: element,
direction: e.scale < 1 ? 1 : -1,
scaleChange: scaleChange,
type: eventType,
isTouchEvent: true
};
event = $.Event(eventType, eventData);
$(element).trigger(event, eventData);
break;
case 'touchstart':
startPoints = {
page: cornerstoneMath.point.pageToPoint(e.originalEvent.touches[0]),
image: cornerstone.pageToPixel(element, e.originalEvent.touches[0].pageX, e.originalEvent.touches[0].pageY),
client: {
x: e.originalEvent.touches[0].clientX,
y: e.originalEvent.touches[0].clientY
}
};
startPoints.canvas = cornerstone.pixelToCanvas(element, startPoints.image);
eventType = 'CornerstoneToolsTouchStart';
eventData = {
event: e.srcEvent,
viewport: cornerstone.getViewport(element),
image: cornerstone.getEnabledElement(element).image,
element: element,
startPoints: startPoints,
currentPoints: startPoints,
type: eventType,
isTouchEvent: true
};
event = $.Event(eventType, eventData);
$(eventData.element).trigger(event, eventData);
if (event.isImmediatePropagationStopped() === false) {
// No current tools responded to the drag action.
// Create new tool measurement
eventType = 'CornerstoneToolsTouchStartActive';
eventData.type = eventType;
$(element).trigger(eventType, eventData);
}
lastPoints = cornerstoneTools.copyPoints(startPoints);
break;
case 'touchend':
startPoints = {
page: cornerstoneMath.point.pageToPoint(e.originalEvent.changedTouches[0]),
image: cornerstone.pageToPixel(element, e.originalEvent.changedTouches[0].pageX, e.originalEvent.changedTouches[0].pageY),
client: {
x: e.originalEvent.changedTouches[0].clientX,
y: e.originalEvent.changedTouches[0].clientY
}
};
startPoints.canvas = cornerstone.pixelToCanvas(element, startPoints.image);
eventType = 'CornerstoneToolsTouchEnd';
eventData = {
event: e.srcEvent,
viewport: cornerstone.getViewport(element),
image: cornerstone.getEnabledElement(element).image,
element: element,
startPoints: startPoints,
currentPoints: startPoints,
type: eventType,
isTouchEvent: true
};
event = $.Event(eventType, eventData);
$(eventData.element).trigger(event, eventData);
break;
case 'panmove':
// calculate our current points in page and image coordinates
currentPoints = {
page: cornerstoneMath.point.pageToPoint(e.pointers[0]),
image: cornerstone.pageToPixel(element, e.pointers[0].pageX, e.pointers[0].pageY),
client: {
x: e.pointers[0].clientX,
y: e.pointers[0].clientY
}
};
currentPoints.canvas = cornerstone.pixelToCanvas(element, currentPoints.image);
// Calculate delta values in page and image coordinates
deltaPoints = {
page: cornerstoneMath.point.subtract(currentPoints.page, lastPoints.page),
image: cornerstoneMath.point.subtract(currentPoints.image, lastPoints.image),
client: cornerstoneMath.point.subtract(currentPoints.client, lastPoints.client),
canvas: cornerstoneMath.point.subtract(currentPoints.canvas, lastPoints.canvas)
};
eventType = 'CornerstoneToolsTouchDrag';
if (e.pointers.length > 1) {
eventType = 'CornerstoneToolsMultiTouchDrag';
}
eventData = {
viewport: cornerstone.getViewport(element),
image: cornerstone.getEnabledElement(element).image,
element: element,
startPoints: startPoints,
lastPoints: lastPoints,
currentPoints: currentPoints,
deltaPoints: deltaPoints,
numPointers: e.pointers.length,
type: eventType,
isTouchEvent: true
};
event = $.Event(eventType, eventData);
$(element).trigger(event, eventData);
lastPoints = cornerstoneTools.copyPoints(currentPoints);
break;
case 'panend':
// If lastPoints is not yet set, it means panend fired without panstart or pan,
// so we can ignore this event
if (!lastPoints) {
return false;
}
currentPoints = {
page: cornerstoneMath.point.pageToPoint(e.pointers[0]),
image: cornerstone.pageToPixel(element, e.pointers[0].pageX, e.pointers[0].pageY),
client: {
x: e.pointers[0].clientX,
y: e.pointers[0].clientY
}
};
currentPoints.canvas = cornerstone.pixelToCanvas(element, currentPoints.image);
// Calculate delta values in page and image coordinates
deltaPoints = {
page: cornerstoneMath.point.subtract(currentPoints.page, lastPoints.page),
image: cornerstoneMath.point.subtract(currentPoints.image, lastPoints.image),
client: cornerstoneMath.point.subtract(currentPoints.client, lastPoints.client),
canvas: cornerstoneMath.point.subtract(currentPoints.canvas, lastPoints.canvas)
};
eventType = 'CornerstoneToolsDragEnd';
eventData = {
event: e.srcEvent,
viewport: cornerstone.getViewport(element),
image: cornerstone.getEnabledElement(element).image,
element: element,
startPoints: startPoints,
lastPoints: lastPoints,
currentPoints: currentPoints,
deltaPoints: deltaPoints,
type: eventType,
isTouchEvent: true
};
event = $.Event(eventType, eventData);
$(element).trigger(event, eventData);
return cornerstoneTools.pauseEvent(e);
case 'rotatemove':
var rotation = e.rotation - lastRotation;
lastRotation = e.rotation;
eventType = 'CornerstoneToolsTouchRotate';
eventData = {
event: e.srcEvent,
viewport: cornerstone.getViewport(element),
image: cornerstone.getEnabledElement(element).image,
element: element,
rotation: rotation,
type: eventType
};
event = $.Event(eventType, eventData);
$(element).trigger(event, eventData);
break;
}
return false;
}
function enable(element) {
disable(element);
var hammerOptions = {
inputClass: Hammer.SUPPORT_POINTER_EVENTS ? Hammer.PointerEventInput : Hammer.TouchInput
};
var mc = new Hammer.Manager(element, hammerOptions);
var panOptions = {
pointers: 0,
direction: Hammer.DIRECTION_ALL,
threshold: 0
};
var pan = new Hammer.Pan(panOptions);
var pinch = new Hammer.Pinch({
threshold: 0
});
var rotate = new Hammer.Rotate({
threshold: 0
});
var press = new Hammer.Press({
threshold: 10
});
// we want to detect both the same time
pinch.recognizeWith(pan);
pinch.recognizeWith(rotate);
// add to the Manager
mc.add([ pan, press, rotate, pinch ]);
mc.on('press tap doubletap panstart panmove panend pinchstart pinchmove rotatemove', onTouch);
$(element).data('hammer', mc);
$(element).on('touchstart touchend', onTouch);
cornerstoneTools.preventGhostClick(element);
}
function disable(element) {
$(element).off('touchstart touchend', onTouch);
var mc = $(element).data('hammer');
if (mc) {
mc.off('press tap doubletap panstart panmove panend pinchmove rotatemove', onTouch);
}
}
// module exports
cornerstoneTools.touchInput = {
enable: enable,
disable: disable
};
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/inputSources/touchInput.js
// Begin Source: src/imageTools/simpleMouseButtonTool.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function simpleMouseButtonTool(mouseDownCallback) {
var configuration = {};
var toolInterface = {
activate: function(element, mouseButtonMask, options) {
$(element).off('CornerstoneToolsMouseDownActivate', mouseDownCallback);
var eventData = {
mouseButtonMask: mouseButtonMask, options: options
};
$(element).on('CornerstoneToolsMouseDownActivate', eventData, mouseDownCallback);
},
disable: function(element) {$(element).off('CornerstoneToolsMouseDownActivate', mouseDownCallback);},
enable: function(element) {$(element).off('CornerstoneToolsMouseDownActivate', mouseDownCallback);},
deactivate: function(element) {$(element).off('CornerstoneToolsMouseDownActivate', mouseDownCallback);},
getConfiguration: function() { return configuration;},
setConfiguration: function(config) {configuration = config;}
};
return toolInterface;
}
// module exports
cornerstoneTools.simpleMouseButtonTool = simpleMouseButtonTool;
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/simpleMouseButtonTool.js
// Begin Source: src/imageTools/mouseButtonTool.js
(function($, cornerstone, cornerstoneMath, cornerstoneTools) {
'use strict';
function mouseButtonTool(mouseToolInterface) {
var configuration = {};
///////// BEGIN ACTIVE TOOL ///////
function addNewMeasurement(mouseEventData) {
var element = mouseEventData.element;
var measurementData = mouseToolInterface.createNewMeasurement(mouseEventData);
if (!measurementData) {
return;
}
var eventData = {
mouseButtonMask: mouseEventData.which,
};
// 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.
$(element).off('CornerstoneToolsMouseMove', mouseToolInterface.mouseMoveCallback || mouseMoveCallback);
$(element).off('CornerstoneToolsMouseDown', mouseToolInterface.mouseDownCallback || mouseDownCallback);
$(element).off('CornerstoneToolsMouseDownActivate', mouseToolInterface.mouseDownActivateCallback || mouseDownActivateCallback);
if (mouseToolInterface.mouseDoubleClickCallback) {
$(element).off('CornerstoneToolsMouseDoubleClick', mouseToolInterface.mouseDoubleClickCallback);
}
cornerstone.updateImage(element);
var handleMover;
if (Object.keys(measurementData.handles).length === 1) {
handleMover = cornerstoneTools.moveHandle;
} else {
handleMover = cornerstoneTools.moveNewHandle;
}
handleMover(mouseEventData, measurementData.handles.end, function() {
measurementData.active = false;
measurementData.invalidated = true;
if (cornerstoneTools.anyHandlesOutsideImage(mouseEventData, measurementData.handles)) {
// delete the measurement
cornerstoneTools.removeToolState(element, mouseToolInterface.toolType, measurementData);
}
$(element).on('CornerstoneToolsMouseMove', eventData, mouseToolInterface.mouseMoveCallback || mouseMoveCallback);
$(element).on('CornerstoneToolsMouseDown', eventData, mouseToolInterface.mouseDownCallback || mouseDownCallback);
$(element).on('CornerstoneToolsMouseDownActivate', eventData, mouseToolInterface.mouseDownActivateCallback || mouseDownActivateCallback);
if (mouseToolInterface.mouseDoubleClickCallback) {
$(element).on('CornerstoneToolsMouseDoubleClick', eventData, mouseToolInterface.mouseDoubleClickCallback);
}
cornerstone.updateImage(element);
});
}
function mouseDownActivateCallback(e, eventData) {
if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) {
if (mouseToolInterface.addNewMeasurement) {
mouseToolInterface.addNewMeasurement(eventData);
} else {
addNewMeasurement(eventData);
}
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
}
///////// END ACTIVE TOOL ///////
///////// BEGIN DEACTIVE TOOL ///////
function mouseMoveCallback(e, eventData) {
cornerstoneTools.toolCoordinates.setCoords(eventData);
// if a mouse button is down, do nothing
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;
for (var i = 0; i < toolData.data.length; i++) {
// get the cursor position in canvas coordinates
var coords = eventData.currentPoints.canvas;
var data = toolData.data[i];
if (cornerstoneTools.handleActivator(eventData.element, data.handles, coords) === true) {
imageNeedsUpdate = true;
}
if ((mouseToolInterface.pointNearTool(eventData.element, data, coords) && !data.active) || (!mouseToolInterface.pointNearTool(eventData.element, data, coords) && data.active)) {
data.active = !data.active;
imageNeedsUpdate = true;
}
}
// Handle activation status changed, redraw the image
if (imageNeedsUpdate === true) {
cornerstone.updateImage(eventData.element);
}
}
function mouseDownCallback(e, eventData) {
var data;
var element = eventData.element;
function handleDoneMove() {
data.active = false;
data.invalidated = true;
if (cornerstoneTools.anyHandlesOutsideImage(eventData, data.handles)) {
// delete the measurement
cornerstoneTools.removeToolState(element, mouseToolInterface.toolType, data);
}
cornerstone.updateImage(element);
$(element).on('CornerstoneToolsMouseMove', eventData, mouseToolInterface.mouseMoveCallback || mouseMoveCallback);
}
if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) {
var coords = eventData.startPoints.canvas;
var toolData = cornerstoneTools.getToolState(e.currentTarget, mouseToolInterface.toolType);
var i;
// now check to see if there is a handle we can move
if (toolData) {
for (i = 0; i < toolData.data.length; i++) {
data = toolData.data[i];
var distanceSq = 25;
var handle = cornerstoneTools.getHandleNearImagePoint(element, data, coords, distanceSq);
if (handle) {
$(element).off('CornerstoneToolsMouseMove', mouseToolInterface.mouseMoveCallback || mouseMoveCallback);
data.active = true;
cornerstoneTools.moveHandle(eventData, handle, handleDoneMove);
e.stopImmediatePropagation();
return false;
}
}
}
// Now check to see if there is a line we can move
// now check to see if we have a tool that we can move
if (toolData && mouseToolInterface.pointNearTool) {
var options = {
deleteIfHandleOutsideImage: true,
preventHandleOutsideImage: false
};
for (i = 0; i < toolData.data.length; i++) {
data = toolData.data[i];
if (mouseToolInterface.pointNearTool(element, data, coords)) {
$(element).off('CornerstoneToolsMouseMove', mouseToolInterface.mouseMoveCallback || mouseMoveCallback);
cornerstoneTools.moveAllHandles(e, data, toolData, options, handleDoneMove);
e.stopImmediatePropagation();
return false;
}
}
}
}
}
///////// END DEACTIVE TOOL ///////
// not visible, not interactive
function disable(element) {
$(element).off('CornerstoneImageRendered', mouseToolInterface.onImageRendered);
$(element).off('CornerstoneToolsMouseMove', mouseToolInterface.mouseMoveCallback || mouseMoveCallback);
$(element).off('CornerstoneToolsMouseDown', mouseToolInterface.mouseDownCallback || mouseDownCallback);
$(element).off('CornerstoneToolsMouseDownActivate', mouseToolInterface.mouseDownActivateCallback || mouseDownActivateCallback);
if (mouseToolInterface.mouseDoubleClickCallback) {
$(element).off('CornerstoneToolsMouseDoubleClick', mouseToolInterface.mouseDoubleClickCallback);
}
cornerstone.updateImage(element);
}
// visible but not interactive
function enable(element) {
$(element).off('CornerstoneImageRendered', mouseToolInterface.onImageRendered);
$(element).off('CornerstoneToolsMouseMove', mouseToolInterface.mouseMoveCallback || mouseMoveCallback);
$(element).off('CornerstoneToolsMouseDown', mouseToolInterface.mouseDownCallback || mouseDownCallback);
$(element).off('CornerstoneToolsMouseDownActivate', mouseToolInterface.mouseDownActivateCallback || mouseDownActivateCallback);
if (mouseToolInterface.mouseDoubleClickCallback) {
$(element).off('CornerstoneToolsMouseDoubleClick', mouseToolInterface.mouseDoubleClickCallback);
}
$(element).on('CornerstoneImageRendered', mouseToolInterface.onImageRendered);
cornerstone.updateImage(element);
}
// visible, interactive and can create
function activate(element, mouseButtonMask) {
var eventData = {
mouseButtonMask: mouseButtonMask,
};
$(element).off('CornerstoneImageRendered', mouseToolInterface.onImageRendered);
$(element).off('CornerstoneToolsMouseMove', mouseToolInterface.mouseMoveCallback || mouseMoveCallback);
$(element).off('CornerstoneToolsMouseDown', mouseToolInterface.mouseDownCallback || mouseDownCallback);
$(element).off('CornerstoneToolsMouseDownActivate', mouseToolInterface.mouseDownActivateCallback || mouseDownActivateCallback);
$(element).on('CornerstoneImageRendered', mouseToolInterface.onImageRendered);
$(element).on('CornerstoneToolsMouseMove', eventData, mouseToolInterface.mouseMoveCallback || mouseMoveCallback);
$(element).on('CornerstoneToolsMouseDown', eventData, mouseToolInterface.mouseDownCallback || mouseDownCallback);
$(element).on('CornerstoneToolsMouseDownActivate', eventData, mouseToolInterface.mouseDownActivateCallback || mouseDownActivateCallback);
if (mouseToolInterface.mouseDoubleClickCallback) {
$(element).off('CornerstoneToolsMouseDoubleClick', mouseToolInterface.mouseDoubleClickCallback);
$(element).on('CornerstoneToolsMouseDoubleClick', eventData, mouseToolInterface.mouseDoubleClickCallback);
}
cornerstone.updateImage(element);
}
// visible, interactive
function deactivate(element, mouseButtonMask) {
var eventData = {
mouseButtonMask: mouseButtonMask,
};
$(element).off('CornerstoneImageRendered', mouseToolInterface.onImageRendered);
$(element).off('CornerstoneToolsMouseMove', mouseToolInterface.mouseMoveCallback || mouseMoveCallback);
$(element).off('CornerstoneToolsMouseDown', mouseToolInterface.mouseDownCallback || mouseDownCallback);
$(element).off('CornerstoneToolsMouseDownActivate', mouseToolInterface.mouseDownActivateCallback || mouseDownActivateCallback);
$(element).on('CornerstoneImageRendered', mouseToolInterface.onImageRendered);
$(element).on('CornerstoneToolsMouseMove', eventData, mouseToolInterface.mouseMoveCallback || mouseMoveCallback);
$(element).on('CornerstoneToolsMouseDown', eventData, mouseToolInterface.mouseDownCallback || mouseDownCallback);
if (mouseToolInterface.mouseDoubleClickCallback) {
$(element).off('CornerstoneToolsMouseDoubleClick', mouseToolInterface.mouseDoubleClickCallback);
$(element).on('CornerstoneToolsMouseDoubleClick', eventData, mouseToolInterface.mouseDoubleClickCallback);
}
cornerstone.updateImage(element);
}
function getConfiguration() {
return configuration;
}
function setConfiguration(config) {
configuration = config;
}
var toolInterface = {
enable: enable,
disable: disable,
activate: activate,
deactivate: deactivate,
getConfiguration: getConfiguration,
setConfiguration: setConfiguration,
mouseDownCallback: mouseDownCallback,
mouseMoveCallback: mouseMoveCallback,
mouseDownActivateCallback: mouseDownActivateCallback
};
// Expose pointNearTool if available
if (mouseToolInterface.pointNearTool) {
toolInterface.pointNearTool = mouseToolInterface.pointNearTool;
}
if (mouseToolInterface.mouseDoubleClickCallback) {
toolInterface.mouseDoubleClickCallback = mouseToolInterface.mouseDoubleClickCallback;
}
if (mouseToolInterface.addNewMeasurement) {
toolInterface.addNewMeasurement = mouseToolInterface.addNewMeasurement;
}
return toolInterface;
}
// module exports
cornerstoneTools.mouseButtonTool = mouseButtonTool;
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/imageTools/mouseButtonTool.js
// Begin Source: src/imageTools/mouseButtonRectangleTool.js
(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;
}
// 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);
cornerstoneTools.moveHandle(mouseEventData, measurementData.handles.end, function() {
measurementData.active = false;
if (cornerstoneTools.anyHandlesOutsideImage(mouseEventData, measurementData.handles)) {
// delete the measurement
cornerstoneTools.removeToolState(mouseEventData.element, mouseToolInterface.toolType, measurementData);
}
$(mouseEventData.element).on('CornerstoneToolsMouseMove', mouseMoveCallback);
}, preventHandleOutsideImage);
}
function mouseDownActivateCallback(e, eventData) {
if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) {
addNewMeasurement(eventData);
return false; // false = cases jquery to preventDefault() and stopPropagation() this event
}
}
///////// END ACTIVE TOOL ///////
///////// BEGIN DEACTIVE TOOL ///////
function mouseMoveCallback(e, eventData) {
cornerstoneTools.toolCoordinates.setCoords(eventData);
// if a mouse button is down, do nothing
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;
var coords = eventData.currentPoints.canvas;
for (var i = 0; i < toolData.data.length; i++) {
// get the cursor position in image coordinates
var data = toolData.data[i];
if (cornerstoneTools.handleActivator(eventData.element, data.handles, coords) === true) {
imageNeedsUpdate = true;
}
if ((mouseToolInterface.pointInsideRect(eventData.element, data, coords) && !data.active) || (!mouseToolInterface.pointInsideRect(eventData.element, data, coords) && data.active)) {
data.active = !data.active;
imageNeedsUpdate = true;
}
}
// Handle activation status changed, redraw the image
if (imageNeedsUpdate === true) {
cornerstone.updateImage(eventData.element);
}
}
function mouseDownCallback(e, eventData) {
var data;
function handleDoneMove() {
data.active = false;
if (cornerstoneTools.anyHandlesOutsideImage(eventData, data.handles)) {
// delete the measurement
cornerstoneTools.removeToolState(eventData.element, mouseToolInterface.toolType, data);
}
cornerstone.updateImage(eventData.element);
$(eventData.element).on('CornerstoneToolsMouseMove', mouseMoveCallback);
}
if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) {
var coords = eventData.startPoints.canvas;
var toolData = cornerstoneTools.getToolState(e.currentTarget, mouseToolInterface.toolType);
var i;
// now check to see if there is a handle we can move
if (toolData !== undefined) {
for (i = 0; i < toolData.data.length; i++) {
data = toolData.data[i];
var handle = cornerstoneTools.getHandleNearImagePoint(eventData.element, data, coords);
if (handle !== undefined) {
$(eventData.element).off('CornerstoneToolsMouseMove', mouseMoveCallback);
data.active = true;
cornerstoneTools.moveHandle(eventData, handle, handleDoneMove, preventHandleOutsideImage);
e.stopImmediatePropagation();
return false;
}
}
}
// Now check to see if there is a line we can move
// now check to see if we have a tool that we can move
if (toolData !== undefined && mouseToolInterface.pointInsideRect !== undefined) {
for (i = 0; i < toolData.data.length; i++) {
data = toolData.data[i];
if (mouseToolInterface.pointInsideRect(eventData.element, data, coords)) {
$(eventData.element).off('CornerstoneToolsMouseMove', mouseMoveCallback);
cornerstoneTools.moveAllHandles(e, data, toolData, false, preventHandleOutsideImage);
$(eventData.element).on('CornerstoneToolsMouseMove', mouseMoveCallback);
e.stopImmediatePropagation();
return false;
}
}
}
}
}
///////// END DEACTIVE TOOL ///////
// not visible, not interactive
function disable(element) {
$(element).off('CornerstoneImageRendered', mouseToolInterface.onImageRendered);
$(element).off('CornerstoneToolsMouseMove', mouseMoveCallback);
$(element).off('CornerstoneToolsMouseDown', mouseDownCallback);
$(element).off('CornerstoneToolsMouseDownActivate', mouseDownActivateCallback);
cornerstone.updateImage(element);
}
// visible but not interactive
function enable(element) {
$(element).off('CornerstoneImageRendered', mouseToolInterface.onImageRendered);
$(element).off('CornerstoneToolsMouseMove', mouseMoveCallback);
$(element).off('CornerstoneToolsMouseDown', mouseDownCallback);
$(element).off('CornerstoneToolsMouseDownActivate', mouseDownActivateCallback);
$(element).on('CornerstoneImageRendered', mouseToolInterface.onImageRendered);
cornerstone.updateImage(element);
}
// visible, interactive and can create
function activate(element, mouseButtonMask) {
var eventData = {
mouseButtonMask: mouseButtonMask,
};
$(element).off('CornerstoneImageRendered', mouseToolInterface.onImageRendered);
$(element).off('CornerstoneToolsMouseMove', mouseMoveCallback);
$(element).off('CornerstoneToolsMouseDown', mouseDownCallback);
$(element).off('CornerstoneToolsMouseDownActivate', mouseDownActivateCallback);
$(element).on('CornerstoneImageRendered', mouseToolInterface.onImageRendered);
$(element).on('CornerstoneToolsMouseMove', eventData, mouseMoveCallback);
$(element).on('CornerstoneToolsMouseDown', eventData, mouseDownCallback);
$(element).on('CornerstoneToolsMouseDownActivate', eventData, mouseDownActivateCallback);
cornerstone.updateImage(element);
}
// visible, interactive
function deactivate(element, mouseButtonMask) {
var eventData = {
mouseButtonMask: mouseButtonMask,
};
$(element).off('CornerstoneImageRendered', mouseToolInterface.onImageRendered);
$(element).off('CornerstoneToolsMouseMove', mouseMoveCallback);
$(element).off('CornerstoneToolsMouseDown', mouseDownCallback);
$(element).off('CornerstoneToolsMouseDownActivate', mouseDownActivateCallback);
$(element).on('CornerstoneImageRendered', mouseToolInterface.onImageRendered);
$(element).on('CornerstoneToolsMouseMove', eventData, mouseMoveCallback);
$(element).on('CornerstoneToolsMouseDown', eventData, mouseDownCallback);
cornerstone.updateImage(element);
}
var toolInterface = {
enable: enable,
disable: disable,
activate: activate,
deactivate: deactivate
};
return toolInterface;
}
// module exports
cornerstoneTools.mouseButtonRectangleTool = mouseButtonRectangleTool;
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/imageTools/mouseButtonRectangleTool.js
// Begin Source: src/imageTools/mouseWheelTool.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function mouseWheelTool(mouseWheelCallback) {
var toolInterface = {
activate: function(element) {
$(element).off('CornerstoneToolsMouseWheel', mouseWheelCallback);
var eventData = {
};
$(element).on('CornerstoneToolsMouseWheel', eventData, mouseWheelCallback);
},
disable: function(element) {$(element).off('CornerstoneToolsMouseWheel', mouseWheelCallback);},
enable: function(element) {$(element).off('CornerstoneToolsMouseWheel', mouseWheelCallback);},
deactivate: function(element) {$(element).off('CornerstoneToolsMouseWheel', mouseWheelCallback);}
};
return toolInterface;
}
// module exports
cornerstoneTools.mouseWheelTool = mouseWheelTool;
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/mouseWheelTool.js
// Begin Source: src/imageTools/touchDragTool.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function touchDragTool(touchDragCallback, options) {
var events = 'CornerstoneToolsTouchDrag';
if (options && options.fireOnTouchStart === true) {
events += ' CornerstoneToolsTouchStart';
}
var toolInterface = {
activate: function(element) {
$(element).off(events, touchDragCallback);
if (options && options.eventData) {
$(element).on(events, options.eventData, touchDragCallback);
} else {
$(element).on(events, touchDragCallback);
}
if (options && options.activateCallback) {
options.activateCallback(element);
}
}, disable: function(element) {
$(element).off(events, touchDragCallback);
if (options && options.disableCallback) {
options.disableCallback(element);
}
}, enable: function(element) {
$(element).off(events, touchDragCallback);
if (options && options.enableCallback) {
options.enableCallback(element);
}
}, deactivate: function(element) {
$(element).off(events, touchDragCallback);
if (options && options.deactivateCallback) {
options.deactivateCallback(element);
}
}
};
return toolInterface;
}
// module exports
cornerstoneTools.touchDragTool = touchDragTool;
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/touchDragTool.js
// Begin Source: src/imageTools/touchPinchTool.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
/*jshint newcap: false */
function touchPinchTool(touchPinchCallback) {
var toolInterface = {
activate: function(element) {
$(element).off('CornerstoneToolsTouchPinch', touchPinchCallback);
var eventData = {
};
$(element).on('CornerstoneToolsTouchPinch', eventData, touchPinchCallback);
},
disable: function(element) {$(element).off('CornerstoneToolsTouchPinch', touchPinchCallback);},
enable: function(element) {$(element).off('CornerstoneToolsTouchPinch', touchPinchCallback);},
deactivate: function(element) {$(element).off('CornerstoneToolsTouchPinch', touchPinchCallback);}
};
return toolInterface;
}
// module exports
cornerstoneTools.touchPinchTool = touchPinchTool;
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/touchPinchTool.js
// Begin Source: src/imageTools/touchTool.js
(function($, cornerstone, cornerstoneMath, cornerstoneTools) {
'use strict';
function deactivateAllHandles(handles) {
Object.keys(handles).forEach(function(name) {
var handle = handles[name];
handle.active = false;
});
}
function deactivateAllToolInstances(toolData) {
if (!toolData) {
return;
}
for (var i = 0; i < toolData.data.length; i++) {
var data = toolData.data[i];
data.active = false;
if (!data.handles) {
continue;
}
deactivateAllHandles(data.handles);
}
}
function touchTool(touchToolInterface) {
///////// BEGIN ACTIVE TOOL ///////
function addNewMeasurement(touchEventData) {
var element = touchEventData.element;
var measurementData = touchToolInterface.createNewMeasurement(touchEventData);
if (!measurementData) {
return;
}
cornerstoneTools.addToolState(element, touchToolInterface.toolType, measurementData);
if (Object.keys(measurementData.handles).length === 1 && touchEventData.type === 'CornerstoneToolsTap') {
measurementData.active = false;
measurementData.handles.end.active = false;
measurementData.handles.end.highlight = false;
measurementData.invalidated = true;
if (cornerstoneTools.anyHandlesOutsideImage(touchEventData, measurementData.handles)) {
// delete the measurement
cornerstoneTools.removeToolState(element, touchToolInterface.toolType, measurementData);
}
cornerstone.updateImage(element);
return;
}
$(element).off('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback);
$(element).off('CornerstoneToolsTouchDrag', touchToolInterface.touchMoveCallback || touchMoveCallback);
$(element).off('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback);
cornerstone.updateImage(element);
cornerstoneTools.moveNewHandleTouch(touchEventData, measurementData.handles.end, function() {
measurementData.active = false;
measurementData.invalidated = true;
if (cornerstoneTools.anyHandlesOutsideImage(touchEventData, measurementData.handles)) {
// delete the measurement
cornerstoneTools.removeToolState(element, touchToolInterface.toolType, measurementData);
}
$(element).on('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback);
$(element).on('CornerstoneToolsTouchDrag', touchToolInterface.touchMoveCallback || touchMoveCallback);
$(element).on('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback);
cornerstone.updateImage(element);
});
}
function touchDownActivateCallback(e, eventData) {
if (touchToolInterface.addNewMeasurement) {
touchToolInterface.addNewMeasurement(eventData);
} else {
addNewMeasurement(eventData);
}
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
///////// END ACTIVE TOOL ///////
///////// BEGIN INACTIVE TOOL ///////
function touchMoveCallback(e, eventData) {
cornerstoneTools.toolCoordinates.setCoords(eventData);
// if we have no tool data for this element, do nothing
var toolData = cornerstoneTools.getToolState(eventData.element, touchToolInterface.toolType);
if (!toolData) {
return;
}
// We have tool data, search through all data
// and see if we can activate a handle
var imageNeedsUpdate = false;
var distanceThreshold = 50;
for (var i = 0; i < toolData.data.length; i++) {
// get the touch position in canvas coordinates
var coords = eventData.currentPoints.canvas;
var data = toolData.data[i];
if (cornerstoneTools.handleActivator(eventData.element, data.handles, coords, distanceThreshold) === true) {
imageNeedsUpdate = true;
}
}
// Handle activation status changed, redraw the image
if (imageNeedsUpdate === true) {
cornerstone.updateImage(eventData.element);
}
return false;
}
function tapCallback(e, eventData) {
var element = eventData.element;
var coords = eventData.currentPoints.canvas;
var toolData = cornerstoneTools.getToolState(e.currentTarget, touchToolInterface.toolType);
var data;
var i;
// Deactivate everything
deactivateAllToolInstances(toolData);
function doneMovingCallback() {
deactivateAllToolInstances(toolData);
if (cornerstoneTools.anyHandlesOutsideImage(eventData, data.handles)) {
// delete the measurement
cornerstoneTools.removeToolState(element, touchToolInterface.toolType, data);
}
cornerstone.updateImage(element);
$(element).on('CornerstoneToolsTouchDrag', touchToolInterface.touchMoveCallback || touchMoveCallback);
$(element).on('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback);
$(element).on('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback);
}
// now check to see if there is a handle we can move
if (toolData) {
for (i = 0; i < toolData.data.length; i++) {
data = toolData.data[i];
var distanceSq = 75; // Should probably make this a settable property later
var handle = cornerstoneTools.getHandleNearImagePoint(element, data, coords, distanceSq);
if (handle) {
$(element).off('CornerstoneToolsTouchDrag', touchToolInterface.touchMoveCallback || touchMoveCallback);
$(element).off('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback);
$(element).off('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback);
data.active = true;
handle.active = true;
cornerstone.updateImage(element);
cornerstoneTools.touchMoveHandle(e, handle, doneMovingCallback);
e.stopImmediatePropagation();
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
}
}
// Now check to see if we have a tool that we can move
if (toolData && touchToolInterface.pointNearTool) {
for (i = 0; i < toolData.data.length; i++) {
data = toolData.data[i];
if (touchToolInterface.pointNearTool(element, data, coords)) {
$(element).off('CornerstoneToolsTouchDrag', touchToolInterface.touchMoveCallback || touchMoveCallback);
$(element).off('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback);
$(element).off('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback);
data.active = true;
cornerstone.updateImage(element);
cornerstoneTools.touchMoveAllHandles(e, data, toolData, true, doneMovingCallback);
e.stopImmediatePropagation();
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
}
}
// If there is nothing to move, add a new instance of the tool
// Need to check here to see if activation is allowed!
if (touchToolInterface.touchDownActivateCallback) {
touchToolInterface.touchDownActivateCallback(e, eventData);
} else {
touchDownActivateCallback(e, eventData);
}
return false;
}
function touchStartCallback(e, eventData) {
var element = eventData.element;
var coords = eventData.startPoints.canvas;
var data;
var toolData = cornerstoneTools.getToolState(e.currentTarget, touchToolInterface.toolType);
var i;
function doneMovingCallback() {
data.active = false;
data.invalidated = true;
if (cornerstoneTools.anyHandlesOutsideImage(eventData, data.handles)) {
// delete the measurement
cornerstoneTools.removeToolState(eventData.element, touchToolInterface.toolType, data);
}
cornerstone.updateImage(eventData.element);
$(element).on('CornerstoneToolsTouchDrag', touchToolInterface.touchMoveCallback || touchMoveCallback);
$(element).on('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback);
$(element).on('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback);
}
// now check to see if there is a handle we can move
var distanceFromTouch = cornerstoneTools.touchSettings.getToolDistanceFromTouch();
var distanceSq = Math.pow(Math.max(Math.abs(distanceFromTouch.x), Math.abs(distanceFromTouch.y)), 2);
if (toolData) {
for (i = 0; i < toolData.data.length; i++) {
data = toolData.data[i];
var handle = cornerstoneTools.getHandleNearImagePoint(eventData.element, data, coords, distanceSq);
if (handle) {
$(element).off('CornerstoneToolsTouchDrag', touchToolInterface.touchMoveCallback || touchMoveCallback);
$(element).off('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback);
$(element).off('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback);
data.active = true;
cornerstoneTools.touchMoveHandle(e, handle, doneMovingCallback);
e.stopImmediatePropagation();
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
}
}
// Now check to see if we have a tool that we can move
if (toolData && touchToolInterface.pointNearTool) {
for (i = 0; i < toolData.data.length; i++) {
data = toolData.data[i];
if (touchToolInterface.pointNearTool(eventData.element, data, coords)) {
$(element).off('CornerstoneToolsTouchDrag', touchToolInterface.touchMoveCallback || touchMoveCallback);
$(element).off('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback);
$(element).off('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback);
cornerstoneTools.touchMoveAllHandles(e, data, toolData, true, doneMovingCallback);
$(element).on('CornerstoneToolsTouchDrag', touchToolInterface.touchMoveCallback || touchMoveCallback);
e.stopImmediatePropagation();
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
}
}
}
///////// END INACTIVE TOOL ///////
// not visible, not interactive
function disable(element) {
$(element).off('CornerstoneImageRendered', touchToolInterface.onImageRendered);
$(element).off('CornerstoneToolsTouchDrag', touchToolInterface.touchMoveCallback || touchMoveCallback);
$(element).off('CornerstoneToolsTouchStart', touchToolInterface.touchStartCallback || touchStartCallback);
$(element).off('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback);
$(element).off('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback);
if (touchToolInterface.doubleTapCallback) {
$(element).off('CornerstoneToolsDoubleTap', touchToolInterface.doubleTapCallback);
}
if (touchToolInterface.pressCallback) {
$(element).off('CornerstoneToolsTouchPress', touchToolInterface.pressCallback);
}
cornerstone.updateImage(element);
}
// visible but not interactive
function enable(element) {
$(element).off('CornerstoneImageRendered', touchToolInterface.onImageRendered);
$(element).off('CornerstoneToolsTouchDrag', touchToolInterface.touchMoveCallback || touchMoveCallback);
$(element).off('CornerstoneToolsTouchStart', touchToolInterface.touchStartCallback || touchStartCallback);
$(element).off('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback);
$(element).off('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback);
$(element).on('CornerstoneImageRendered', touchToolInterface.onImageRendered);
if (touchToolInterface.doubleTapCallback) {
$(element).off('CornerstoneToolsDoubleTap', touchToolInterface.doubleTapCallback);
}
if (touchToolInterface.pressCallback) {
$(element).off('CornerstoneToolsTouchPress', touchToolInterface.pressCallback);
}
cornerstone.updateImage(element);
}
// visible, interactive and can create
function activate(element) {
$(element).off('CornerstoneImageRendered', touchToolInterface.onImageRendered);
$(element).off('CornerstoneToolsTouchDrag', touchToolInterface.touchMoveCallback || touchMoveCallback);
$(element).off('CornerstoneToolsTouchStart', touchToolInterface.touchStartCallback || touchStartCallback);
$(element).off('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback);
$(element).off('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback);
$(element).on('CornerstoneImageRendered', touchToolInterface.onImageRendered);
$(element).on('CornerstoneToolsTouchDrag', touchToolInterface.touchMoveCallback || touchMoveCallback);
$(element).on('CornerstoneToolsTouchStart', touchToolInterface.touchStartCallback || touchStartCallback);
$(element).on('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback);
$(element).on('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback);
if (touchToolInterface.doubleTapCallback) {
$(element).off('CornerstoneToolsDoubleTap', touchToolInterface.doubleTapCallback);
$(element).on('CornerstoneToolsDoubleTap', touchToolInterface.doubleTapCallback);
}
if (touchToolInterface.pressCallback) {
$(element).off('CornerstoneToolsTouchPress', touchToolInterface.pressCallback);
$(element).on('CornerstoneToolsTouchPress', touchToolInterface.pressCallback);
}
cornerstone.updateImage(element);
}
// visible, interactive
function deactivate(element) {
$(element).off('CornerstoneImageRendered', touchToolInterface.onImageRendered);
$(element).off('CornerstoneToolsTouchDrag', touchToolInterface.touchMoveCallback || touchMoveCallback);
$(element).off('CornerstoneToolsTouchStart', touchToolInterface.touchStartCallback || touchStartCallback);
$(element).off('CornerstoneToolsTouchStartActive', touchToolInterface.touchDownActivateCallback || touchDownActivateCallback);
$(element).off('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback);
$(element).on('CornerstoneImageRendered', touchToolInterface.onImageRendered);
$(element).on('CornerstoneToolsTouchDrag', touchToolInterface.touchMoveCallback || touchMoveCallback);
//$(element).on('CornerstoneToolsTap', touchToolInterface.tapCallback || tapCallback);
if (touchToolInterface.doubleTapCallback) {
$(element).off('CornerstoneToolsDoubleTap', touchToolInterface.doubleTapCallback);
$(element).on('CornerstoneToolsDoubleTap', touchToolInterface.doubleTapCallback);
}
if (touchToolInterface.pressCallback) {
$(element).off('CornerstoneToolsTouchPress', touchToolInterface.pressCallback);
$(element).on('CornerstoneToolsTouchPress', touchToolInterface.pressCallback);
}
cornerstone.updateImage(element);
}
var toolInterface = {
enable: enable,
disable: disable,
activate: activate,
deactivate: deactivate,
touchStartCallback: touchToolInterface.touchStartCallback || touchStartCallback,
touchMoveCallback: touchToolInterface.touchMoveCallback || touchMoveCallback,
touchDownActivateCallback: touchToolInterface.touchDownActivateCallback || touchDownActivateCallback,
tapCallback: touchToolInterface.tapCallback || tapCallback
};
if (touchToolInterface.doubleTapCallback) {
toolInterface.doubleTapCallback = touchToolInterface.doubleTapCallback;
}
if (touchToolInterface.pressCallback) {
toolInterface.pressCallback = touchToolInterface.pressCallback;
}
if (touchToolInterface.addNewMeasurement) {
toolInterface.addNewMeasurement = touchToolInterface.addNewMeasurement;
}
return toolInterface;
}
// module exports
cornerstoneTools.touchTool = touchTool;
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/imageTools/touchTool.js
// Begin Source: src/imageTools/AngleTool.js
(function($, cornerstone, cornerstoneMath, cornerstoneTools) {
'use strict';
var toolType = 'angle';
///////// BEGIN ACTIVE TOOL ///////
function createNewMeasurement(mouseEventData) {
// create the measurement data for this tool with the end handle activated
var angleData = {
visible: true,
active: true,
handles: {
start: {
x: mouseEventData.currentPoints.image.x - 20,
y: mouseEventData.currentPoints.image.y + 10,
highlight: true,
active: false
},
end: {
x: mouseEventData.currentPoints.image.x,
y: mouseEventData.currentPoints.image.y,
highlight: true,
active: true
},
start2: {
x: mouseEventData.currentPoints.image.x - 20,
y: mouseEventData.currentPoints.image.y + 10,
highlight: true,
active: false
},
end2: {
x: mouseEventData.currentPoints.image.x,
y: mouseEventData.currentPoints.image.y + 20,
highlight: true,
active: false
}
}
};
return angleData;
}
///////// END ACTIVE TOOL ///////
function pointNearTool(element, data, coords) {
var lineSegment = {
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;
}
lineSegment.start = cornerstone.pixelToCanvas(element, data.handles.start2);
lineSegment.end = cornerstone.pixelToCanvas(element, data.handles.end2);
distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, coords);
return (distanceToPoint < 5);
}
///////// 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 === undefined) {
return;
}
// 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
var color;
var lineWidth = cornerstoneTools.toolStyle.getToolWidth();
var font = cornerstoneTools.textStyle.getFont();
var config = cornerstoneTools.angle.getConfiguration();
for (var i = 0; i < toolData.data.length; i++) {
context.save();
// configurable shadow
if (config && config.shadow) {
context.shadowColor = '#000000';
context.shadowOffsetX = +1;
context.shadowOffsetY = +1;
}
var data = toolData.data[i];
//differentiate the color of activation tool
if (data.active) {
color = cornerstoneTools.toolColors.getActiveColor();
} else {
color = cornerstoneTools.toolColors.getToolColor();
}
// draw the line
context.beginPath();
context.strokeStyle = color;
context.lineWidth = lineWidth;
var handleStartCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.start);
var handleEndCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.end);
context.moveTo(handleStartCanvas.x, handleStartCanvas.y);
context.lineTo(handleEndCanvas.x, handleEndCanvas.y);
handleStartCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.start2);
handleEndCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.end2);
context.moveTo(handleStartCanvas.x, handleStartCanvas.y);
context.lineTo(handleEndCanvas.x, handleEndCanvas.y);
context.stroke();
// draw the handles
cornerstoneTools.drawHandles(context, eventData, data.handles);
// Draw the text
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.
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;
var dy2 = (Math.ceil(data.handles.start2.y) - Math.ceil(data.handles.end2.y)) * eventData.image.rowPixelSpacing;
var angle = Math.acos(Math.abs(((dx1 * dx2) + (dy1 * dy2)) / (Math.sqrt((dx1 * dx1) + (dy1 * dy1)) * Math.sqrt((dx2 * dx2) + (dy2 * dy2)))));
angle = angle * (180 / Math.PI);
var rAngle = cornerstoneTools.roundToDecimal(angle, 2);
var str = '00B0'; // degrees symbol
var text = rAngle.toString() + String.fromCharCode(parseInt(str, 16));
var textX = (handleStartCanvas.x + handleEndCanvas.x) / 2;
var textY = (handleStartCanvas.y + handleEndCanvas.y) / 2;
context.font = font;
cornerstoneTools.drawTextBox(context, text, textX, textY, color);
context.restore();
}
}
///////// END IMAGE RENDERING ///////
// module exports
cornerstoneTools.angle = cornerstoneTools.mouseButtonTool({
createNewMeasurement: createNewMeasurement,
onImageRendered: onImageRendered,
pointNearTool: pointNearTool,
toolType: toolType
});
cornerstoneTools.angleTouch = cornerstoneTools.touchTool({
createNewMeasurement: createNewMeasurement,
onImageRendered: onImageRendered,
pointNearTool: pointNearTool,
toolType: toolType
});
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/imageTools/AngleTool.js
// Begin Source: src/imageTools/annotation.js
(function($, cornerstone, cornerstoneMath, cornerstoneTools) {
'use strict';
var toolType = 'arrowAnnotate';
// Define a callback to get your text annotation
// This could be used, e.g. to open a modal
function getTextCallback(doneChangingTextCallback) {
doneChangingTextCallback(prompt('Enter your annotation:'));
}
function changeTextCallback(data, doneChangingTextCallback) {
doneChangingTextCallback(prompt('Change your annotation:'));
}
var configuration = {
getTextCallback: getTextCallback,
changeTextCallback: changeTextCallback,
drawHandles: false,
drawHandlesOnHover: true,
arrowFirst: true
};
/// --- Mouse Tool --- ///
///////// BEGIN ACTIVE TOOL ///////
function addNewMeasurement(mouseEventData) {
function doneChangingTextCallback(text) {
if (text !== null) {
measurementData.text = text;
} else {
cornerstoneTools.removeToolState(mouseEventData.element, toolType, measurementData);
}
measurementData.active = false;
cornerstone.updateImage(mouseEventData.element);
}
var measurementData = createNewMeasurement(mouseEventData);
var eventData = {
mouseButtonMask: mouseEventData.which,
};
// 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);
$(mouseEventData.element).off('CornerstoneToolsMouseDown', cornerstoneTools.arrowAnnotate.mouseDownCallback);
$(mouseEventData.element).off('CornerstoneToolsMouseDownActivate', cornerstoneTools.arrowAnnotate.mouseDownActivateCallback);
cornerstone.updateImage(mouseEventData.element);
cornerstoneTools.moveNewHandle(mouseEventData, measurementData.handles.end, function() {
if (cornerstoneTools.anyHandlesOutsideImage(mouseEventData, measurementData.handles)) {
// delete the measurement
cornerstoneTools.removeToolState(mouseEventData.element, toolType, measurementData);
}
var config = cornerstoneTools.arrowAnnotate.getConfiguration();
if (measurementData.text === undefined) {
config.getTextCallback(doneChangingTextCallback);
}
$(mouseEventData.element).on('CornerstoneToolsMouseMove', eventData, cornerstoneTools.arrowAnnotate.mouseMoveCallback);
$(mouseEventData.element).on('CornerstoneToolsMouseDown', eventData, cornerstoneTools.arrowAnnotate.mouseDownCallback);
$(mouseEventData.element).on('CornerstoneToolsMouseDownActivate', eventData, cornerstoneTools.arrowAnnotate.mouseDownActivateCallback);
cornerstone.updateImage(mouseEventData.element);
});
}
function createNewMeasurement(mouseEventData) {
// create the measurement data for this tool with the end handle activated
var measurementData = {
visible: true,
active: true,
handles: {
start: {
x: mouseEventData.currentPoints.image.x,
y: mouseEventData.currentPoints.image.y,
highlight: true,
active: false
},
end: {
x: mouseEventData.currentPoints.image.x,
y: mouseEventData.currentPoints.image.y,
highlight: true,
active: false
}
}
};
return measurementData;
}
///////// END ACTIVE TOOL ///////
function pointNearTool(element, data, coords) {
var lineSegment = {
start: cornerstone.pixelToCanvas(element, data.handles.start),
end: cornerstone.pixelToCanvas(element, data.handles.end)
};
var distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, coords);
return (distanceToPoint < 25);
}
function drawArrow(context, start, end, color, lineWidth) {
//variables to be used when creating the arrow
var headLength = 10;
var angle = Math.atan2(end.y - start.y, end.x - start.x);
//starting path of the arrow from the start square to the end square and drawing the stroke
context.beginPath();
context.moveTo(start.x, start.y);
context.lineTo(end.x, end.y);
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.stroke();
//starting a new path from the head of the arrow to one of the sides of the point
context.beginPath();
context.moveTo(end.x, end.y);
context.lineTo(end.x - headLength * Math.cos(angle - Math.PI / 7), end.y - headLength * Math.sin(angle - Math.PI / 7));
//path from the side point of the arrow, to the other side point
context.lineTo(end.x - headLength * Math.cos(angle + Math.PI / 7), end.y - headLength * Math.sin(angle + Math.PI / 7));
//path from the side point back to the tip of the arrow, and then again to the opposite side point
context.lineTo(end.x, end.y);
context.lineTo(end.x - headLength * Math.cos(angle - Math.PI / 7), end.y - headLength * Math.sin(angle - Math.PI / 7));
//draws the paths created above
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.stroke();
context.fillStyle = color;
context.fill();
}
///////// 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 === undefined) {
return;
}
// 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);
var color;
var lineWidth = cornerstoneTools.toolStyle.getToolWidth();
var font = cornerstoneTools.textStyle.getFont();
var config = cornerstoneTools.arrowAnnotate.getConfiguration();
for (var i = 0; i < toolData.data.length; i++) {
context.save();
var data = toolData.data[i];
if (data.active) {
color = cornerstoneTools.toolColors.getActiveColor();
} 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);
if (config.arrowFirst) {
drawArrow(context, handleEndCanvas, handleStartCanvas, color, lineWidth);
} else {
drawArrow(context, handleStartCanvas, handleEndCanvas, color, lineWidth);
}
if (config.drawHandles) {
cornerstoneTools.drawHandles(context, eventData, data.handles, color);
} else if (config.drawHandlesOnHover && data.handles.start.active) {
cornerstoneTools.drawHandles(context, eventData, [ data.handles.start ], color);
} else if (config.drawHandlesOnHover && data.handles.end.active) {
cornerstoneTools.drawHandles(context, eventData, [ data.handles.end ], color);
}
// Draw the text
if (data.text && data.text !== '') {
context.font = font;
var distance = 13;
// TODO: add 2 dimensional vector operations to cornerstoneMath
var vector;
var displacement = {
x: distance,
y: distance / 2
};
vector = {
x: handleEndCanvas.x - handleStartCanvas.x,
y: handleEndCanvas.y - handleStartCanvas.y
};
var textCoords;
if (config.arrowFirst) {
// Fix text placement if arrow faces right
if (vector.x < 0) {
displacement.x = -displacement.x - context.measureText(data.text).width;
}
textCoords = {
x: vector.x + handleStartCanvas.x + displacement.x,
y: vector.y + handleStartCanvas.y + displacement.y
};
} else {
// Fix text placement if arrow faces right
if (vector.x > 0) {
displacement.x = -displacement.x - context.measureText(data.text).width;
}
textCoords = {
x: -vector.x + handleEndCanvas.x + displacement.x,
y: -vector.y + handleEndCanvas.y + displacement.y
};
}
cornerstoneTools.drawTextBox(context, data.text, textCoords.x, textCoords.y, color);
}
context.restore();
}
}
// ---- Touch tool ----
///////// BEGIN ACTIVE TOOL ///////
function addNewMeasurementTouch(touchEventData) {
var element = touchEventData.element;
function doneChangingTextCallback(text) {
if (text !== null) {
measurementData.text = text;
} else {
cornerstoneTools.removeToolState(element, toolType, measurementData);
}
measurementData.active = false;
cornerstone.updateImage(element);
}
var measurementData = createNewMeasurement(touchEventData);
cornerstoneTools.addToolState(element, toolType, measurementData);
$(element).off('CornerstoneToolsTouchDrag', cornerstoneTools.arrowAnnotateTouch.touchMoveCallback);
$(element).off('CornerstoneToolsDragStartActive', cornerstoneTools.arrowAnnotateTouch.touchDownActivateCallback);
$(element).off('CornerstoneToolsTap', cornerstoneTools.arrowAnnotateTouch.tapCallback);
cornerstone.updateImage(element);
cornerstoneTools.moveNewHandleTouch(touchEventData, measurementData.handles.end, function() {
cornerstone.updateImage(element);
if (cornerstoneTools.anyHandlesOutsideImage(touchEventData, measurementData.handles)) {
// delete the measurement
cornerstoneTools.removeToolState(element, toolType, measurementData);
}
var config = cornerstoneTools.arrowAnnotate.getConfiguration();
if (measurementData.text === undefined) {
config.getTextCallback(doneChangingTextCallback);
}
$(element).on('CornerstoneToolsTouchDrag', cornerstoneTools.arrowAnnotateTouch.touchMoveCallback);
$(element).on('CornerstoneToolsDragStartActive', cornerstoneTools.arrowAnnotateTouch.touchDownActivateCallback);
$(element).on('CornerstoneToolsTap', cornerstoneTools.arrowAnnotateTouch.tapCallback);
});
}
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.arrowAnnotate.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)) {
data.active = true;
cornerstone.updateImage(element);
// Allow relabelling via a callback
config.changeTextCallback(data, doneChangingTextCallback);
e.stopImmediatePropagation();
return false;
}
}
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
cornerstoneTools.arrowAnnotate = cornerstoneTools.mouseButtonTool({
addNewMeasurement: addNewMeasurement,
createNewMeasurement: createNewMeasurement,
onImageRendered: onImageRendered,
pointNearTool: pointNearTool,
toolType: toolType,
mouseDoubleClickCallback: doubleClickCallback
});
cornerstoneTools.arrowAnnotate.setConfiguration(configuration);
cornerstoneTools.arrowAnnotateTouch = cornerstoneTools.touchTool({
addNewMeasurement: addNewMeasurementTouch,
createNewMeasurement: createNewMeasurement,
onImageRendered: onImageRendered,
pointNearTool: pointNearTool,
toolType: toolType,
pressCallback: doubleClickCallback
});
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/imageTools/annotation.js
// Begin Source: src/imageTools/crosshairs.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
var toolType = 'crosshairs';
function chooseLocation(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;
}
// Get current element target information
var sourceElement = e.currentTarget;
var sourceEnabledElement = cornerstone.getEnabledElement(sourceElement);
var sourceImageId = sourceEnabledElement.image.imageId;
var sourceImagePlane = cornerstoneTools.metaData.get('imagePlane', sourceImageId);
// Get currentPoints from mouse cursor on selected element
var sourceImagePoint = eventData.currentPoints.image;
// Transfer this to a patientPoint given imagePlane metadata
var patientPoint = cornerstoneTools.imagePointToPatientPoint(sourceImagePoint, sourceImagePlane);
// Get the enabled elements associated with this synchronization context
var syncContext = toolData.data[0].synchronizationContext;
var enabledElements = syncContext.getSourceElements();
// Iterate over each synchronized element
$.each(enabledElements, function(index, targetElement) {
// don't do anything if the target is the same as the source
if (targetElement === sourceElement) {
return; // Same as 'continue' in a normal for loop
}
var minDistance = Number.MAX_VALUE;
var newImageIdIndex = -1;
var stackToolDataSource = cornerstoneTools.getToolState(targetElement, 'stack');
if (stackToolDataSource === undefined) {
return; // Same as 'continue' in a normal for loop
}
var stackData = stackToolDataSource.data[0];
// Find within the element's stack the closest image plane to selected location
$.each(stackData.imageIds, function(index, imageId) {
var imagePlane = cornerstoneTools.metaData.get('imagePlane', imageId);
var imagePosition = imagePlane.imagePositionPatient;
var row = imagePlane.rowCosines.clone();
var column = imagePlane.columnCosines.clone();
var normal = column.clone().cross(row.clone());
var distance = Math.abs(normal.clone().dot(imagePosition) - normal.clone().dot(patientPoint));
//console.log(index + '=' + distance);
if (distance < minDistance) {
minDistance = distance;
newImageIdIndex = index;
}
});
if (newImageIdIndex === stackData.currentImageIdIndex) {
return;
}
// Switch the loaded image to the required image
if (newImageIdIndex !== -1 && stackData.imageIds[newImageIdIndex] !== undefined) {
var startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler();
var endLoadingHandler = cornerstoneTools.loadHandlerManager.getEndLoadHandler();
var errorLoadingHandler = cornerstoneTools.loadHandlerManager.getErrorLoadingHandler();
if (startLoadingHandler) {
startLoadingHandler(targetElement);
}
cornerstone.loadAndCacheImage(stackData.imageIds[newImageIdIndex]).then(function(image) {
var viewport = cornerstone.getViewport(targetElement);
stackData.currentImageIdIndex = newImageIdIndex;
cornerstone.displayImage(targetElement, image, viewport);
if (endLoadingHandler) {
endLoadingHandler(targetElement);
}
}, function(error) {
var imageId = stackData.imageIds[newImageIdIndex];
if (errorLoadingHandler) {
errorLoadingHandler(targetElement, imageId, error);
}
});
}
});
}
function mouseUpCallback(e, eventData) {
$(eventData.element).off('CornerstoneToolsMouseDrag', mouseDragCallback);
$(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback);
}
function mouseDownCallback(e, eventData) {
if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) {
$(eventData.element).on('CornerstoneToolsMouseDrag', mouseDragCallback);
$(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback);
chooseLocation(e, eventData);
return false; // false = cases jquery to preventDefault() and stopPropagation() this event
}
}
function mouseDragCallback(e, eventData) {
chooseLocation(e, eventData);
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
function enable(element, mouseButtonMask, synchronizationContext) {
var eventData = {
mouseButtonMask: mouseButtonMask,
};
// Clear any currently existing toolData
var toolData = cornerstoneTools.getToolState(element, toolType);
toolData = [];
cornerstoneTools.addToolState(element, toolType, {
synchronizationContext: synchronizationContext,
});
$(element).off('CornerstoneToolsMouseDown', mouseDownCallback);
$(element).on('CornerstoneToolsMouseDown', eventData, mouseDownCallback);
}
// disables the reference line tool for the given element
function disable(element) {
$(element).off('CornerstoneToolsMouseDown', mouseDownCallback);
}
// module/private exports
cornerstoneTools.crosshairs = {
activate: enable,
deactivate: disable,
enable: enable,
disable: disable
};
function dragEndCallback(e, eventData) {
$(eventData.element).off('CornerstoneToolsTouchDrag', dragCallback);
$(eventData.element).off('CornerstoneToolsDragEnd', dragEndCallback);
}
function dragStartCallback(e, eventData) {
$(eventData.element).on('CornerstoneToolsTouchDrag', dragCallback);
$(eventData.element).on('CornerstoneToolsDragEnd', dragEndCallback);
chooseLocation(e, eventData);
return false;
}
function dragCallback(e, eventData) {
chooseLocation(e, eventData);
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
function enableTouch(element, synchronizationContext) {
// Clear any currently existing toolData
var toolData = cornerstoneTools.getToolState(element, toolType);
toolData = [];
cornerstoneTools.addToolState(element, toolType, {
synchronizationContext: synchronizationContext,
});
$(element).off('CornerstoneToolsTouchStart', dragStartCallback);
$(element).on('CornerstoneToolsTouchStart', dragStartCallback);
}
// disables the reference line tool for the given element
function disableTouch(element) {
$(element).off('CornerstoneToolsTouchStart', dragStartCallback);
}
cornerstoneTools.crosshairsTouch = {
activate: enableTouch,
deactivate: disableTouch,
enable: enableTouch,
disable: disableTouch
};
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/crosshairs.js
// Begin Source: src/imageTools/displayTool.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function displayTool(onImageRendered) {
var configuration = {};
var toolInterface = {
disable: function(element) {$(element).off('CornerstoneImageRendered', onImageRendered);},
enable: function(element) {
$(element).off('CornerstoneImageRendered', onImageRendered);
$(element).on('CornerstoneImageRendered', onImageRendered);
cornerstone.updateImage(element);
},
getConfiguration: function() { return configuration; },
setConfiguration: function(config) {configuration = config;}
};
return toolInterface;
}
// module exports
cornerstoneTools.displayTool = displayTool;
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/displayTool.js
// Begin Source: src/imageTools/dragProbe.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function defaultStrategy(eventData) {
var enabledElement = cornerstone.getEnabledElement(eventData.element);
cornerstone.updateImage(eventData.element);
var context = enabledElement.canvas.getContext('2d');
context.setTransform(1, 0, 0, 1, 0, 0);
var color = cornerstoneTools.toolColors.getActiveColor();
var font = cornerstoneTools.textStyle.getFont();
var fontHeight = cornerstoneTools.textStyle.getFontSize();
context.save();
var x = Math.round(eventData.currentPoints.image.x);
var y = Math.round(eventData.currentPoints.image.y);
var storedPixels;
var text,
str;
if (x < 0 || y < 0 || x >= eventData.image.columns || y >= eventData.image.rows) {
return;
}
if (eventData.image.color) {
storedPixels = cornerstoneTools.getRGBPixels(eventData.element, x, y, 1, 1);
text = '' + x + ', ' + y;
str = 'R: ' + storedPixels[0] + ' G: ' + storedPixels[1] + ' B: ' + storedPixels[2] + ' A: ' + storedPixels[3];
} else {
storedPixels = cornerstone.getStoredPixels(eventData.element, x, y, 1, 1);
var sp = storedPixels[0];
var mo = sp * eventData.image.slope + eventData.image.intercept;
var suv = cornerstoneTools.calculateSUV(eventData.image, sp);
// Draw text
text = '' + x + ', ' + y;
str = 'SP: ' + sp + ' MO: ' + parseFloat(mo.toFixed(3));
if (suv) {
str += ' SUV: ' + parseFloat(suv.toFixed(3));
}
}
// Draw text
var coords = {
// translate the x/y away from the cursor
x: eventData.currentPoints.image.x + 3,
y: eventData.currentPoints.image.y - 3
};
var textCoords = cornerstone.pixelToCanvas(eventData.element, coords);
context.font = font;
context.fillStyle = color;
cornerstoneTools.drawTextBox(context, str, textCoords.x, textCoords.y + fontHeight + 5, color);
cornerstoneTools.drawTextBox(context, text, textCoords.x, textCoords.y, color);
context.restore();
}
function minimalStrategy(eventData) {
var element = eventData.element;
var enabledElement = cornerstone.getEnabledElement(element);
var image = enabledElement.image;
cornerstone.updateImage(element);
var context = enabledElement.canvas.getContext('2d');
context.setTransform(1, 0, 0, 1, 0, 0);
var color = cornerstoneTools.toolColors.getActiveColor();
var font = cornerstoneTools.textStyle.getFont();
context.save();
var toolCoords;
if (eventData.isTouchEvent === true) {
var ratio = image.height / enabledElement.canvas.height;
console.log ('Image to canvas height ratio: ' + ratio);
toolCoords = {
x: eventData.currentPoints.image.x,
y: eventData.currentPoints.image.y - cornerstoneTools.textStyle.getFontSize() * 4 * ratio
};
} else {
toolCoords = eventData.currentPoints.image;
}
var storedPixels;
var text;
if (toolCoords.x < 0 || toolCoords.y < 0 ||
toolCoords.x >= image.columns || toolCoords.y >= image.rows) {
return;
}
if (image.color) {
storedPixels = cornerstone.getStoredPixels(element, toolCoords.x, toolCoords.y, 3, 1);
text = 'R: ' + storedPixels[0] + ' G: ' + storedPixels[1] + ' B: ' + storedPixels[2];
} else {
storedPixels = cornerstone.getStoredPixels(element, toolCoords.x, toolCoords.y, 1, 1);
var huValue = storedPixels[0] * image.slope + image.intercept;
text = parseFloat(huValue.toFixed(3));
}
// Prepare text
var textCoords = cornerstone.pixelToCanvas(element, toolCoords);
context.font = font;
context.fillStyle = color;
// Translate the x/y away from the cursor
var translation;
if (eventData.isTouchEvent === true) {
var width = context.measureText(text).width;
translation = {
x: -width / 2 - 5,
y: -cornerstoneTools.textStyle.getFontSize() * 1.5
};
var handleRadius = 6;
context.beginPath();
context.strokeStyle = color;
context.arc(textCoords.x, textCoords.y, handleRadius, 0, 2 * Math.PI);
context.stroke();
} else {
translation = {
x: 4,
y: -4
};
}
cornerstoneTools.drawTextBox(context, text, textCoords.x + translation.x, textCoords.y + translation.y, color);
context.restore();
}
function mouseUpCallback(e, eventData) {
$(eventData.element).off('CornerstoneToolsMouseDrag', dragCallback);
$(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback);
cornerstone.updateImage(eventData.element);
}
function mouseDownCallback(e, eventData) {
if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) {
$(eventData.element).on('CornerstoneToolsMouseDrag', dragCallback);
$(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback);
cornerstoneTools.dragProbe.strategy(eventData);
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
}
function dragCallback(e, eventData) {
cornerstoneTools.dragProbe.strategy(eventData);
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
cornerstoneTools.dragProbe = cornerstoneTools.simpleMouseButtonTool(mouseDownCallback);
cornerstoneTools.dragProbe.strategies = {
default: defaultStrategy,
minimal: minimalStrategy
};
cornerstoneTools.dragProbe.strategy = defaultStrategy;
var options = {
fireOnTouchStart: true
};
cornerstoneTools.dragProbeTouch = cornerstoneTools.touchDragTool(dragCallback, options);
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/dragProbe.js
// Begin Source: src/imageTools/ellipticalRoi.js
(function($, cornerstone, cornerstoneMath, cornerstoneTools) {
'use strict';
var toolType = 'ellipticalRoi';
///////// BEGIN ACTIVE TOOL ///////
function createNewMeasurement(mouseEventData) {
// create the measurement data for this tool with the end handle activated
var measurementData = {
visible: true,
active: true,
invalidated: true,
handles: {
start: {
x: mouseEventData.currentPoints.image.x,
y: mouseEventData.currentPoints.image.y,
highlight: true,
active: false
},
end: {
x: mouseEventData.currentPoints.image.x,
y: mouseEventData.currentPoints.image.y,
highlight: true,
active: true
}
}
};
return measurementData;
}
///////// END ACTIVE TOOL ///////
///////// BEGIN IMAGE RENDERING ///////
function pointInEllipse(ellipse, location) {
var xRadius = ellipse.width / 2;
var yRadius = ellipse.height / 2;
if (xRadius <= 0.0 || yRadius <= 0.0) {
return false;
}
var center = {
x: ellipse.left + xRadius,
y: ellipse.top + yRadius
};
/* This is a more general form of the circle equation
*
* X^2/a^2 + Y^2/b^2 <= 1
*/
var normalized = {
x: location.x - center.x,
y: location.y - center.y
};
var inEllipse = ((normalized.x * normalized.x) / (xRadius * xRadius)) + ((normalized.y * normalized.y) / (yRadius * yRadius)) <= 1.0;
return inEllipse;
}
function calculateMeanStdDev(sp, ellipse) {
// TODO: Get a real statistics library here that supports large counts
var sum = 0;
var sumSquared = 0;
var count = 0;
var index = 0;
for (var y = ellipse.top; y < ellipse.top + ellipse.height; y++) {
for (var x = ellipse.left; x < ellipse.left + ellipse.width; x++) {
if (pointInEllipse(ellipse, {
x: x,
y: y
}) === true) {
sum += sp[index];
sumSquared += sp[index] * sp[index];
count++;
}
index++;
}
}
if (count === 0) {
return {
count: count,
mean: 0.0,
variance: 0.0,
stdDev: 0.0
};
}
var mean = sum / count;
var variance = sumSquared / count - mean * mean;
return {
count: count,
mean: mean,
variance: variance,
stdDev: Math.sqrt(variance)
};
}
function pointNearEllipse(element, data, coords, distance) {
var startCanvas = cornerstone.pixelToCanvas(element, data.handles.start);
var endCanvas = cornerstone.pixelToCanvas(element, data.handles.end);
var minorEllipse = {
left: Math.min(startCanvas.x, endCanvas.x) + distance / 2,
top: Math.min(startCanvas.y, endCanvas.y) + distance / 2 ,
width: Math.abs(startCanvas.x - endCanvas.x) - distance,
height: Math.abs(startCanvas.y - endCanvas.y) - distance
};
var majorEllipse = {
left: Math.min(startCanvas.x, endCanvas.x) - distance / 2,
top: Math.min(startCanvas.y, endCanvas.y) - distance / 2 ,
width: Math.abs(startCanvas.x - endCanvas.x) + distance,
height: Math.abs(startCanvas.y - endCanvas.y) + distance
};
var pointInMinorEllipse = pointInEllipse(minorEllipse, coords);
var pointInMajorEllipse = pointInEllipse(majorEllipse, coords);
if (pointInMajorEllipse && !pointInMinorEllipse) {
return true;
}
return false;
}
function pointNearTool(element, data, coords) {
return pointNearEllipse(element, data, coords, 15);
}
function pointNearToolTouch(element, data, coords) {
return pointNearEllipse(element, data, coords, 25);
}
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 === undefined) {
return;
}
// 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
var color;
var lineWidth = cornerstoneTools.toolStyle.getToolWidth();
var font = cornerstoneTools.textStyle.getFont();
var fontHeight = cornerstoneTools.textStyle.getFontSize();
for (var i = 0; i < toolData.data.length; i++) {
context.save();
var data = toolData.data[i];
//differentiate the color of activation tool
if (data.active) {
color = cornerstoneTools.toolColors.getActiveColor();
} else {
color = cornerstoneTools.toolColors.getToolColor();
}
// draw the ellipse
var handleStartCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.start);
var handleEndCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.end);
var widthCanvas = Math.abs(handleStartCanvas.x - handleEndCanvas.x);
var heightCanvas = Math.abs(handleStartCanvas.y - handleEndCanvas.y);
var leftCanvas = Math.min(handleStartCanvas.x, handleEndCanvas.x);
var topCanvas = Math.min(handleStartCanvas.y, handleEndCanvas.y);
var centerX = (handleStartCanvas.x + handleEndCanvas.x) / 2;
var centerY = (handleStartCanvas.y + handleEndCanvas.y) / 2;
context.beginPath();
context.strokeStyle = color;
context.lineWidth = lineWidth;
cornerstoneTools.drawEllipse(context, leftCanvas, topCanvas, widthCanvas, heightCanvas);
context.closePath();
// draw the handles
cornerstoneTools.drawHandles(context, eventData, data.handles, color);
context.font = font;
var textX,
textY,
area,
meanStdDev;
if (!data.invalidated) {
textX = data.textX;
textY = data.textY;
meanStdDev = data.meanStdDev;
area = data.area;
} else {
// TODO: calculate this in web worker for large pixel counts...
var width = Math.abs(data.handles.start.x - data.handles.end.x);
var height = Math.abs(data.handles.start.y - data.handles.end.y);
var left = Math.min(data.handles.start.x, data.handles.end.x);
var top = Math.min(data.handles.start.y, data.handles.end.y);
var pixels = cornerstone.getPixels(eventData.element, left, top, width, height);
var ellipse = {
left: left,
top: top,
width: width,
height: height
};
// Calculate the mean, stddev, and area
meanStdDev = calculateMeanStdDev(pixels, ellipse);
area = Math.PI * (width * eventData.image.columnPixelSpacing / 2) * (height * eventData.image.rowPixelSpacing / 2);
data.invalidated = false;
if (!isNaN(area)) {
data.area = area;
}
if (!isNaN(meanStdDev.mean) && !isNaN(meanStdDev.stdDev)) {
data.meanStdDev = meanStdDev;
}
}
// Draw text
var areaText,
areaTextWidth = 0;
if (area !== undefined) {
areaText = 'Area: ' + area.toFixed(2) + ' mm' + String.fromCharCode(178);
areaTextWidth = context.measureText(areaText).width;
}
var meanText = 'Mean: ' + meanStdDev.mean.toFixed(2);
var meanTextWidth = context.measureText(meanText).width;
var stdDevText = 'StdDev: ' + meanStdDev.stdDev.toFixed(2);
var stdDevTextWidth = context.measureText(stdDevText).width;
var longestTextWidth = Math.max(meanTextWidth, areaTextWidth, stdDevTextWidth);
textX = centerX < (eventData.image.columns / 2) ? centerX + (widthCanvas / 2) + longestTextWidth: centerX - (widthCanvas / 2) - longestTextWidth - 15;
textY = centerY < (eventData.image.rows / 2) ? centerY + (heightCanvas / 2): centerY - (heightCanvas / 2);
context.fillStyle = color;
if (meanStdDev) {
cornerstoneTools.drawTextBox(context, meanText, textX, textY - fontHeight - 5, color);
cornerstoneTools.drawTextBox(context, stdDevText, textX, textY, color);
}
// Char code 178 is a superscript 2 for mm^2
if (area !== undefined && !isNaN(area)) {
cornerstoneTools.drawTextBox(context, areaText, textX, textY + fontHeight + 5, color);
}
context.restore();
}
}
///////// END IMAGE RENDERING ///////
// module exports
cornerstoneTools.ellipticalRoi = cornerstoneTools.mouseButtonTool({
createNewMeasurement: createNewMeasurement,
onImageRendered: onImageRendered,
pointNearTool: pointNearTool,
toolType: toolType
});
cornerstoneTools.ellipticalRoiTouch = cornerstoneTools.touchTool({
createNewMeasurement: createNewMeasurement,
onImageRendered: onImageRendered,
pointNearTool: pointNearToolTouch,
toolType: toolType
});
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/imageTools/ellipticalRoi.js
// Begin Source: src/imageTools/freehand.js
(function($, cornerstone, cornerstoneMath, cornerstoneTools) {
'use strict';
var toolType = 'freehand';
var configuration = {
mouseLocation: {
handles: {
start: {
highlight: true,
active: true,
}
}
},
freehand: false,
modifying: false,
currentHandle: 0,
currentTool: -1
};
///////// BEGIN ACTIVE TOOL ///////
function addPoint(eventData) {
var toolData = cornerstoneTools.getToolState(eventData.element, toolType);
if (toolData === undefined) {
return;
}
var config = cornerstoneTools.freehand.getConfiguration();
// Get the toolData from the last-drawn drawing
// (this should change when modification is added)
var data = toolData.data[config.currentTool];
var handleData = {
x: eventData.currentPoints.image.x,
y: eventData.currentPoints.image.y,
highlight: true,
active: true,
lines: []
};
// If this is not the first handle
if (data.handles.length){
// Add the line from the current handle to the new handle
data.handles[config.currentHandle - 1].lines.push(eventData.currentPoints.image);
}
// Add the new handle
data.handles.push(handleData);
// Increment the current handle value
config.currentHandle += 1;
// Reset freehand value
config.freehand = false;
// Force onImageRendered to fire
cornerstone.updateImage(eventData.element);
}
function pointNearHandle(eventData, toolIndex) {
var toolData = cornerstoneTools.getToolState(eventData.element, toolType);
if (toolData === undefined) {
return;
}
var data = toolData.data[toolIndex];
if (data.handles === undefined) {
return;
}
var mousePoint = eventData.currentPoints.canvas;
for (var i = 0; i < data.handles.length; i++) {
var handleCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles[i]);
if (cornerstoneMath.point.distance(handleCanvas, mousePoint) < 5) {
return i;
}
}
return;
}
function pointNearHandleAllTools(eventData) {
var toolData = cornerstoneTools.getToolState(eventData.element, toolType);
if (toolData === undefined) {
return;
}
var handleNearby;
for (var toolIndex = 0; toolIndex < toolData.data.length; toolIndex++) {
handleNearby = pointNearHandle(eventData, toolIndex);
if (handleNearby !== undefined) {
return {
handleNearby: handleNearby,
toolIndex: toolIndex
};
}
}
return; // Maybe this should return false?
}
// --- Drawing loop ---
// On first click, add point
// After first click, on mouse move, record location
// If mouse comes close to previous point, snap to it
// On next click, add another point -- continuously
// On each click, if it intersects with a current point, end drawing loop
function mouseUpCallback(e, eventData) {
$(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback);
// Check if drawing is finished
var toolData = cornerstoneTools.getToolState(eventData.element, toolType);
if (toolData === undefined) {
return;
}
var config = cornerstoneTools.freehand.getConfiguration();
if (!eventData.event.shiftKey) {
config.freehand = false;
}
cornerstone.updateImage(eventData.element);
}
function mouseMoveCallback(e, eventData) {
var toolData = cornerstoneTools.getToolState(eventData.element, toolType);
if (toolData === undefined) {
return;
}
var config = cornerstoneTools.freehand.getConfiguration();
var data = toolData.data[config.currentTool];
// Set the mouseLocation handle
config.mouseLocation.handles.start.x = eventData.currentPoints.image.x;
config.mouseLocation.handles.start.y = eventData.currentPoints.image.y;
var currentHandle = config.currentHandle;
if (config.modifying) {
// Move the handle
data.active = true;
data.highlight = true;
data.handles[currentHandle].x = config.mouseLocation.handles.start.x;
data.handles[currentHandle].y = config.mouseLocation.handles.start.y;
if (currentHandle) {
var lastLineIndex = data.handles[currentHandle - 1].lines.length - 1;
var lastLine = data.handles[currentHandle - 1].lines[lastLineIndex];
lastLine.x = config.mouseLocation.handles.start.x;
lastLine.y = config.mouseLocation.handles.start.y;
}
}
if (config.freehand) {
data.handles[currentHandle - 1].lines.push(eventData.currentPoints.image);
} else {
// No snapping in freehand mode
var handleNearby = pointNearHandle(eventData, config.currentTool);
// If there is a handle nearby to snap to
// (and it's not the actual mouse handle)
if (handleNearby !== undefined && handleNearby < (data.handles.length - 1)) {
config.mouseLocation.handles.start.x = data.handles[handleNearby].x;
config.mouseLocation.handles.start.y = data.handles[handleNearby].y;
}
}
// Force onImageRendered
cornerstone.updateImage(eventData.element);
}
function startDrawing(eventData) {
$(eventData.element).on('CornerstoneToolsMouseMove', mouseMoveCallback);
$(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback);
var measurementData = {
visible: true,
active: true,
handles: [],
};
var config = cornerstoneTools.freehand.getConfiguration();
config.mouseLocation.handles.start.x = eventData.currentPoints.image.x;
config.mouseLocation.handles.start.y = eventData.currentPoints.image.y;
cornerstoneTools.addToolState(eventData.element, toolType, measurementData);
var toolData = cornerstoneTools.getToolState(eventData.element, toolType);
config.currentTool = toolData.data.length - 1;
}
function endDrawing(eventData, handleNearby) {
var toolData = cornerstoneTools.getToolState(eventData.element, toolType);
if (toolData === undefined) {
return;
}
var config = cornerstoneTools.freehand.getConfiguration();
var data = toolData.data[config.currentTool];
data.active = false;
data.highlight = false;
// Connect the end of the drawing to the handle nearest to the click
if (handleNearby !== undefined){
data.handles[config.currentHandle - 1].lines.push(data.handles[handleNearby]);
}
if (config.modifying) {
config.modifying = false;
}
// Reset the current handle
config.currentHandle = 0;
config.currentTool = -1;
$(eventData.element).off('CornerstoneToolsMouseMove', mouseMoveCallback);
cornerstone.updateImage(eventData.element);
}
function mouseDownCallback(e, eventData) {
if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) {
var toolData = cornerstoneTools.getToolState(eventData.element, toolType);
var handleNearby, toolIndex;
var config = cornerstoneTools.freehand.getConfiguration();
var currentTool = config.currentTool;
if (config.modifying) {
endDrawing(eventData);
return;
}
if (currentTool < 0) {
var nearby = pointNearHandleAllTools(eventData);
if (nearby) {
handleNearby = nearby.handleNearby;
toolIndex = nearby.toolIndex;
// This means the user is trying to modify a point
if (handleNearby !== undefined) {
$(eventData.element).on('CornerstoneToolsMouseMove', mouseMoveCallback);
$(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback);
config.modifying = true;
config.currentHandle = handleNearby;
config.currentTool = toolIndex;
}
} else {
startDrawing(eventData);
addPoint(eventData);
}
} else if (currentTool >= 0 && toolData.data[currentTool].active) {
handleNearby = pointNearHandle(eventData, currentTool);
if (handleNearby !== undefined) {
endDrawing(eventData, handleNearby);
} else if (eventData.event.shiftKey) {
config.freehand = true;
} else {
addPoint(eventData);
}
}
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
}
///////// END ACTIVE TOOL ///////
///////// 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 === undefined) {
return;
}
var config = cornerstoneTools.freehand.getConfiguration();
// 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);
var color;
var lineWidth = cornerstoneTools.toolStyle.getToolWidth();
var fillColor = cornerstoneTools.toolColors.getFillColor();
for (var i = 0; i < toolData.data.length; i++) {
context.save();
var data = toolData.data[i];
if (data.active) {
color = cornerstoneTools.toolColors.getActiveColor();
fillColor = cornerstoneTools.toolColors.getFillColor();
} else {
color = cornerstoneTools.toolColors.getToolColor();
fillColor = cornerstoneTools.toolColors.getToolColor();
}
var handleStart;
if (data.handles.length) {
for (var j = 0; j < data.handles.length; j++) {
// Draw a line between handle j and j+1
handleStart = data.handles[j];
var handleStartCanvas = cornerstone.pixelToCanvas(eventData.element, handleStart);
context.beginPath();
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.moveTo(handleStartCanvas.x, handleStartCanvas.y);
for (var k = 0; k < data.handles[j].lines.length; k++) {
var lineCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles[j].lines[k]);
context.lineTo(lineCanvas.x, lineCanvas.y);
context.stroke();
}
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
// the mouse location
context.lineTo(mouseLocationCanvas.x, mouseLocationCanvas.y);
context.stroke();
}
}
}
}
// If the tool is active, draw a handle at the cursor location
if (data.active){
cornerstoneTools.drawHandles(context, eventData, config.mouseLocation.handles, color, fillColor);
}
// draw the handles
cornerstoneTools.drawHandles(context, eventData, data.handles, color, fillColor);
context.restore();
}
}
///////// END IMAGE RENDERING ///////
function enable(element) {
$(element).off('CornerstoneToolsMouseDown', mouseDownCallback);
$(element).off('CornerstoneToolsMouseUp', mouseUpCallback);
$(element).off('CornerstoneToolsMouseMove', mouseMoveCallback);
$(element).off('CornerstoneImageRendered', onImageRendered);
$(element).on('CornerstoneImageRendered', onImageRendered);
cornerstone.updateImage(element);
}
// disables the reference line tool for the given element
function disable(element) {
$(element).off('CornerstoneToolsMouseDown', mouseDownCallback);
$(element).off('CornerstoneToolsMouseUp', mouseUpCallback);
$(element).off('CornerstoneToolsMouseMove', mouseMoveCallback);
$(element).off('CornerstoneImageRendered', onImageRendered);
cornerstone.updateImage(element);
}
// visible and interactive
function activate(element, mouseButtonMask) {
var eventData = {
mouseButtonMask: mouseButtonMask,
};
$(element).off('CornerstoneToolsMouseDown', eventData, mouseDownCallback);
$(element).off('CornerstoneToolsMouseUp', mouseUpCallback);
$(element).off('CornerstoneToolsMouseMove', mouseMoveCallback);
$(element).off('CornerstoneImageRendered', onImageRendered);
$(element).on('CornerstoneImageRendered', onImageRendered);
$(element).on('CornerstoneToolsMouseDown', eventData, mouseDownCallback);
cornerstone.updateImage(element);
}
// visible, but not interactive
function deactivate(element) {
$(element).off('CornerstoneToolsMouseDown', mouseDownCallback);
$(element).off('CornerstoneToolsMouseUp', mouseUpCallback);
$(element).off('CornerstoneToolsMouseMove', mouseMoveCallback);
$(element).off('CornerstoneImageRendered', onImageRendered);
$(element).on('CornerstoneImageRendered', onImageRendered);
cornerstone.updateImage(element);
}
function getConfiguration() {
return configuration;
}
function setConfiguration(config) {
configuration = config;
}
// module/private exports
cornerstoneTools.freehand = {
enable: enable,
disable: disable,
activate: activate,
deactivate: deactivate,
getConfiguration: getConfiguration,
setConfiguration: setConfiguration
};
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/imageTools/freehand.js
// Begin Source: src/imageTools/highlight.js
(function($, cornerstone, cornerstoneMath, cornerstoneTools) {
'use strict';
var toolType = 'highlight';
///////// BEGIN ACTIVE TOOL ///////
function createNewMeasurement(mouseEventData) {
//if already a highlight measurement, creating a new one will be useless
var existingToolData = cornerstoneTools.getToolState(mouseEventData.event.currentTarget, toolType);
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,
active: true,
handles: {
start: {
x: mouseEventData.currentPoints.image.x,
y: mouseEventData.currentPoints.image.y,
highlight: true,
active: false
},
end: {
x: mouseEventData.currentPoints.image.x,
y: mouseEventData.currentPoints.image.y,
highlight: true,
active: true
}
}
};
return measurementData;
}
///////// END ACTIVE TOOL ///////
function pointInsideRect(element, data, coords) {
var startCanvas = cornerstone.pixelToCanvas(element, data.handles.start);
var endCanvas = cornerstone.pixelToCanvas(element, data.handles.end);
var rect = {
left: Math.min(startCanvas.x, endCanvas.x),
top: Math.min(startCanvas.y, endCanvas.y),
width: Math.abs(startCanvas.x - endCanvas.x),
height: Math.abs(startCanvas.y - endCanvas.y)
};
var insideBox = false;
if ((coords.x >= rect.left && coords.x <= (rect.left + rect.width)) && coords.y >= rect.top && coords.y <= (rect.top + rect.height)) {
insideBox = true;
}
return insideBox;
}
function pointNearTool(element, data, coords) {
var startCanvas = cornerstone.pixelToCanvas(element, data.handles.start);
var endCanvas = cornerstone.pixelToCanvas(element, data.handles.end);
var rect = {
left: Math.min(startCanvas.x, endCanvas.x),
top: Math.min(startCanvas.y, endCanvas.y),
width: Math.abs(startCanvas.x - endCanvas.x),
height: Math.abs(startCanvas.y - endCanvas.y)
};
var distanceToPoint = cornerstoneMath.rect.distanceToPoint(rect, coords);
return (distanceToPoint < 5);
}
///////// 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 === undefined) {
return;
}
// we have tool data for this elemen
var context = eventData.canvasContext.canvas.getContext('2d');
context.setTransform(1, 0, 0, 1, 0, 0);
var color;
var lineWidth = cornerstoneTools.toolStyle.getToolWidth();
context.save();
var data = toolData.data[0];
if (!data) {
return;
}
if (data.active) {
color = cornerstoneTools.toolColors.getActiveColor();
} else {
color = cornerstoneTools.toolColors.getToolColor();
}
var handleStartCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.start);
var handleEndCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.end);
var rect = {
left: Math.min(handleStartCanvas.x, handleEndCanvas.x),
top: Math.min(handleStartCanvas.y, handleEndCanvas.y),
width: Math.abs(handleStartCanvas.x - handleEndCanvas.x),
height: Math.abs(handleStartCanvas.y - handleEndCanvas.y)
};
// draw dark fill outside the rectangle
context.beginPath();
context.strokeStyle = 'transparent';
context.rect(0, 0, context.canvas.clientWidth, context.canvas.clientHeight);
context.rect(rect.width + rect.left, rect.top, -rect.width, rect.height);
context.stroke();
context.fillStyle = 'rgba(0,0,0,0.7)';
context.fill();
context.closePath();
// draw dashed stroke rectangle
context.beginPath();
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.setLineDash([ 4 ]);
context.strokeRect(rect.left, rect.top, rect.width, rect.height);
// 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();
}
///////// END IMAGE RENDERING ///////
// module exports
var preventHandleOutsideImage = true;
cornerstoneTools.highlight = cornerstoneTools.mouseButtonRectangleTool({
createNewMeasurement: createNewMeasurement,
onImageRendered: onImageRendered,
pointNearTool: pointNearTool,
pointInsideRect: pointInsideRect,
toolType: toolType
}, preventHandleOutsideImage);
cornerstoneTools.highlightTouch = cornerstoneTools.touchTool({
createNewMeasurement: createNewMeasurement,
onImageRendered: onImageRendered,
pointNearTool: pointNearTool,
pointInsideRect: pointInsideRect,
toolType: toolType
}, preventHandleOutsideImage);
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/imageTools/highlight.js
// Begin Source: src/imageTools/keyboardTool.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function keyboardTool(keyDownCallback) {
var configuration = {};
var toolInterface = {
activate: function(element) {
$(element).off('CornerstoneToolsKeyDown', keyDownCallback);
$(element).on('CornerstoneToolsKeyDown', keyDownCallback);
},
disable: function(element) {$(element).off('CornerstoneToolsKeyDown', keyDownCallback);},
enable: function(element) {$(element).off('CornerstoneToolsKeyDown', keyDownCallback);},
deactivate: function(element) {$(element).off('CornerstoneToolsKeyDown', keyDownCallback);},
getConfiguration: function() { return configuration; },
setConfiguration: function(config) {configuration = config;}
};
return toolInterface;
}
// module exports
cornerstoneTools.keyboardTool = keyboardTool;
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/keyboardTool.js
// Begin Source: src/imageTools/lengthTool.js
(function($, cornerstone, cornerstoneMath, cornerstoneTools) {
'use strict';
var toolType = 'length';
///////// BEGIN ACTIVE TOOL ///////
function createNewMeasurement(mouseEventData) {
// create the measurement data for this tool with the end handle activated
var measurementData = {
visible: true,
active: true,
handles: {
start: {
x: mouseEventData.currentPoints.image.x,
y: mouseEventData.currentPoints.image.y,
highlight: true,
active: false
},
end: {
x: mouseEventData.currentPoints.image.x,
y: mouseEventData.currentPoints.image.y,
highlight: true,
active: true
}
}
};
return measurementData;
}
///////// END ACTIVE TOOL ///////
function pointNearTool(element, data, coords) {
var lineSegment = {
start: cornerstone.pixelToCanvas(element, data.handles.start),
end: cornerstone.pixelToCanvas(element, data.handles.end)
};
var distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, 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 === undefined) {
return;
}
// 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);
var color;
var lineWidth = cornerstoneTools.toolStyle.getToolWidth();
var font = cornerstoneTools.textStyle.getFont();
var config = cornerstoneTools.length.getConfiguration();
for (var i = 0; i < toolData.data.length; i++) {
context.save();
// configurable shadow
if (config && config.shadow) {
context.shadowColor = '#000000';
context.shadowOffsetX = +1;
context.shadowOffsetY = +1;
}
var data = toolData.data[i];
if (data.active) {
color = cornerstoneTools.toolColors.getActiveColor();
} else {
color = cornerstoneTools.toolColors.getToolColor();
}
// draw the line
var handleStartCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.start);
var handleEndCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.end);
context.beginPath();
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.moveTo(handleStartCanvas.x, handleStartCanvas.y);
context.lineTo(handleEndCanvas.x, handleEndCanvas.y);
context.stroke();
// draw the handles
cornerstoneTools.drawHandles(context, eventData, data.handles, color);
// Draw the text
context.fillStyle = color;
context.font = font;
var suffix = ' mm';
if (!eventData.image.rowPixelSpacing || !eventData.image.columnPixelSpacing) {
suffix = ' pixels';
}
var vector = {
x: handleStartCanvas.x - handleEndCanvas.x,
y: handleStartCanvas.y - handleEndCanvas.y,
};
var midpoint = {
x: (handleStartCanvas.x + handleEndCanvas.x) / 2,
y: (handleStartCanvas.y + handleEndCanvas.y) / 2
};
var magnitude = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
var normal = {
x: -vector.y / magnitude,
y: vector.x / magnitude
};
// Set rowPixelSpacing and columnPixelSpacing to 1 if they are undefined (or zero)
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);
var length = Math.sqrt(dx * dx + dy * dy);
var text = '' + length.toFixed(2) + suffix;
var fontSize = cornerstoneTools.textStyle.getFontSize();
var textWidth = context.measureText(text).width;
var distance = {
x: Math.min(10, textWidth / 2),
y: Math.min(25, fontSize),
};
if (handleEndCanvas.y < handleStartCanvas.y) {
distance.x = -distance.x;
distance.y = -distance.y;
}
var textCoords = {
x: midpoint.x + normal.x * distance.x,
y: midpoint.y + normal.y * distance.y
};
cornerstoneTools.drawTextBox(context, text, textCoords.x, textCoords.y, color);
context.restore();
}
}
///////// END IMAGE RENDERING ///////
// module exports
cornerstoneTools.length = cornerstoneTools.mouseButtonTool({
createNewMeasurement: createNewMeasurement,
onImageRendered: onImageRendered,
pointNearTool: pointNearTool,
toolType: toolType
});
cornerstoneTools.lengthTouch = cornerstoneTools.touchTool({
createNewMeasurement: createNewMeasurement,
onImageRendered: onImageRendered,
pointNearTool: pointNearTool,
toolType: toolType
});
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/imageTools/lengthTool.js
// Begin Source: src/imageTools/magnify.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
var configuration = {
magnifySize: 100,
magnificationLevel: 2,
};
var browserName;
/** Remove the magnifying glass when the mouse event ends */
function mouseUpCallback(e, eventData) {
$(eventData.element).off('CornerstoneToolsMouseDrag', dragCallback);
$(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback);
hideTool(eventData);
}
function hideTool(eventData) {
$(eventData.element).find('.magnifyTool').hide();
// Re-enable the mouse cursor
document.body.style.cursor = 'default';
}
/** Draw the magnifying glass on mouseDown, and begin tracking mouse movements */
function mouseDownCallback(e, eventData) {
if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) {
$(eventData.element).on('CornerstoneToolsMouseDrag', eventData, dragCallback);
$(eventData.element).on('CornerstoneToolsMouseUp', eventData, mouseUpCallback);
drawMagnificationTool(eventData);
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
}
function dragEndCallback(e, eventData) {
$(eventData.element).off('CornerstoneToolsDragEnd', dragEndCallback);
$(eventData.element).off('CornerstoneToolsTouchEnd', dragEndCallback);
hideTool(eventData);
}
/** Drag callback is triggered by both the touch and mouse magnify tools */
function dragCallback(e, eventData) {
drawMagnificationTool(eventData);
if (eventData.isTouchEvent === true) {
$(eventData.element).on('CornerstoneToolsDragEnd', dragEndCallback);
$(eventData.element).on('CornerstoneToolsTouchEnd', dragEndCallback);
}
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
/** Draws the magnifying glass */
function drawMagnificationTool(eventData) {
var magnify = $(eventData.element).find('.magnifyTool').get(0);
if (!magnify) {
createMagnificationCanvas(eventData.element);
}
var config = cornerstoneTools.magnify.getConfiguration();
var magnifySize = config.magnifySize;
var magnificationLevel = config.magnificationLevel;
// The 'not' magnifyTool class here is necessary because cornerstone places
// no classes of it's own on the canvas we want to select
var canvas = $(eventData.element).find('canvas').not('.magnifyTool').get(0);
var context = canvas.getContext('2d');
context.setTransform(1, 0, 0, 1, 0, 0);
var zoomCtx = magnify.getContext('2d');
zoomCtx.setTransform(1, 0, 0, 1, 0, 0);
// Calculate the on-canvas location of the mouse pointer / touch
var canvasLocation = cornerstone.pixelToCanvas(eventData.element, eventData.currentPoints.image);
canvasLocation.x = Math.max(canvasLocation.x, 0);
canvasLocation.x = Math.min(canvasLocation.x, canvas.width);
canvasLocation.y = Math.max(canvasLocation.y, 0);
canvasLocation.y = Math.min(canvasLocation.y, canvas.height);
// Clear the rectangle
zoomCtx.clearRect(0, 0, magnifySize, magnifySize);
zoomCtx.fillStyle = 'transparent';
// Fill it with the pixels that the mouse is clicking on
zoomCtx.fillRect(0, 0, magnifySize, magnifySize);
var getSize = magnifySize / magnificationLevel;
var copyFrom = {
x: canvasLocation.x - 0.5 * getSize,
y: canvasLocation.y - 0.5 * getSize
};
if (browserName === 'Safari') {
// Safari breaks when trying to copy pixels with negative indices
// This prevents proper Magnify usage
copyFrom.x = Math.max(copyFrom.x, 0);
copyFrom.y = Math.max(copyFrom.y, 0);
}
copyFrom.x = Math.min(copyFrom.x, canvas.width);
copyFrom.y = Math.min(copyFrom.y, canvas.height);
var scaledMagnify = {
x: (canvas.width - copyFrom.x) * magnificationLevel,
y: (canvas.height - copyFrom.y) * magnificationLevel
};
zoomCtx.drawImage(canvas, copyFrom.x, copyFrom.y, canvas.width - copyFrom.x, canvas.height - copyFrom.y, 0, 0, scaledMagnify.x, scaledMagnify.y);
if (eventData.isTouchEvent === true) {
magnify.style.top = canvasLocation.y - 1 * magnifySize + 'px';
magnify.style.left = canvasLocation.x - 0.5 * magnifySize + 'px';
} else {
// Place the magnification tool at the same location as the pointer
magnify.style.top = canvasLocation.y - 0.5 * magnifySize + 'px';
magnify.style.left = canvasLocation.x - 0.5 * magnifySize + 'px';
}
magnify.style.display = 'block';
// Hide the mouse cursor, so the user can see better
document.body.style.cursor = 'none';
}
/** Creates the magnifying glass canvas */
function createMagnificationCanvas(element) {
// If the magnifying glass canvas doesn't already exist
if ($(element).find('.magnifyTool').length === 0) {
// Create a canvas and append it as a child to the element
var magnify = document.createElement('canvas');
// The magnifyTool class is used to find the canvas later on
magnify.classList.add('magnifyTool');
var config = cornerstoneTools.magnify.getConfiguration();
magnify.width = config.magnifySize;
magnify.height = config.magnifySize;
// Make sure position is absolute so the canvas can follow the mouse / touch
magnify.style.position = 'absolute';
element.appendChild(magnify);
}
}
/** Find the magnifying glass canvas and remove it */
function removeMagnificationCanvas(element) {
$(element).find('.magnifyTool').remove();
}
// --- Mouse tool activate / disable --- //
function disable(element) {
$(element).off('CornerstoneToolsMouseDown', mouseDownCallback);
removeMagnificationCanvas(element);
}
function enable(element) {
var config = cornerstoneTools.magnify.getConfiguration(config);
if (!browserName) {
var infoString = cornerstoneTools.getBrowserInfo();
var info = infoString.split(' ');
browserName = info[0];
}
createMagnificationCanvas(element);
}
function activate(element, mouseButtonMask) {
var eventData = {
mouseButtonMask: mouseButtonMask,
};
$(element).off('CornerstoneToolsMouseDown', mouseDownCallback);
$(element).on('CornerstoneToolsMouseDown', eventData, mouseDownCallback);
createMagnificationCanvas(element);
}
// --- Touch tool activate / disable --- //
function getConfiguration() {
return configuration;
}
function setConfiguration(config) {
configuration = config;
}
// module exports
cornerstoneTools.magnify = {
enable: enable,
activate: activate,
deactivate: disable,
disable: disable,
getConfiguration: getConfiguration,
setConfiguration: setConfiguration
};
var options = {
fireOnTouchStart: true,
activateCallback: createMagnificationCanvas,
disableCallback: removeMagnificationCanvas
};
cornerstoneTools.magnifyTouchDrag = cornerstoneTools.touchDragTool(dragCallback, options);
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/magnify.js
// Begin Source: src/imageTools/multiTouchDragTool.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function multiTouchDragTool(touchDragCallback) {
var configuration = {};
var events = 'CornerstoneToolsMultiTouchDrag';
var toolInterface = {
activate: function(element) {
$(element).off(events, touchDragCallback);
$(element).on(events, touchDragCallback);
},
disable: function(element) {
$(element).off(events, touchDragCallback);
},
enable: function(element) {
$(element).off(events, touchDragCallback);
},
deactivate: function(element) {
$(element).off(events, touchDragCallback);
},
getConfiguration: function() {
return configuration;
},
setConfiguration: function(config) {
configuration = config;
}
};
return toolInterface;
}
// module exports
cornerstoneTools.multiTouchDragTool = multiTouchDragTool;
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/multiTouchDragTool.js
// Begin Source: src/imageTools/orientationMarkers.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function getOrientationMarkers(element) {
var enabledElement = cornerstone.getEnabledElement(element);
var imagePlaneMetaData = cornerstoneTools.metaData.get('imagePlane', enabledElement.image.imageId);
if (!imagePlaneMetaData || !imagePlaneMetaData.rowCosines || !imagePlaneMetaData.columnCosines) {
return;
}
var rowString = cornerstoneTools.orientation.getOrientationString(imagePlaneMetaData.rowCosines);
var columnString = cornerstoneTools.orientation.getOrientationString(imagePlaneMetaData.columnCosines);
var oppositeRowString = cornerstoneTools.orientation.invertOrientationString(rowString);
var oppositeColumnString = cornerstoneTools.orientation.invertOrientationString(columnString);
return {
top: oppositeColumnString,
bottom: columnString,
left: oppositeRowString,
right: rowString
};
}
function getOrientationMarkerPositions(element) {
var enabledElement = cornerstone.getEnabledElement(element);
var coords;
coords = {
x: enabledElement.image.width / 2,
y: 5
};
var topCoords = cornerstone.pixelToCanvas(element, coords);
coords = {
x: enabledElement.image.width / 2,
y: enabledElement.image.height - 5
};
var bottomCoords = cornerstone.pixelToCanvas(element, coords);
coords = {
x: 5,
y: enabledElement.image.height / 2
};
var leftCoords = cornerstone.pixelToCanvas(element, coords);
coords = {
x: enabledElement.image.width - 10,
y: enabledElement.image.height / 2
};
var rightCoords = cornerstone.pixelToCanvas(element, coords);
return {
top: topCoords,
bottom: bottomCoords,
left: leftCoords,
right: rightCoords
};
}
function onImageRendered(e, eventData) {
var element = eventData.element;
var markers = getOrientationMarkers(element);
if (!markers) {
return;
}
var coords = getOrientationMarkerPositions(element, markers);
var context = eventData.canvasContext.canvas.getContext('2d');
context.setTransform(1, 0, 0, 1, 0, 0);
var color = cornerstoneTools.toolColors.getToolColor();
var textWidths = {
top: context.measureText(markers.top).width,
left: context.measureText(markers.left).width,
right: context.measureText(markers.right).width,
bottom: context.measureText(markers.bottom).width
};
cornerstoneTools.drawTextBox(context, markers.top, coords.top.x - textWidths.top / 2, coords.top.y, color);
cornerstoneTools.drawTextBox(context, markers.left, coords.left.x - textWidths.left / 2, coords.left.y, color);
var config = cornerstoneTools.orientationMarkers.getConfiguration();
if (config && config.drawAllMarkers) {
cornerstoneTools.drawTextBox(context, markers.right, coords.right.x - textWidths.right / 2, coords.right.y, color);
cornerstoneTools.drawTextBox(context, markers.bottom, coords.bottom.x - textWidths.bottom / 2, coords.bottom.y, color);
}
}
///////// END IMAGE RENDERING ///////
// module exports
cornerstoneTools.orientationMarkers = cornerstoneTools.displayTool(onImageRendered);
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/orientationMarkers.js
// Begin Source: src/imageTools/pan.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function mouseUpCallback(e, eventData) {
$(eventData.element).off('CornerstoneToolsMouseDrag', dragCallback);
$(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback);
}
function mouseDownCallback(e, eventData) {
if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) {
$(eventData.element).on('CornerstoneToolsMouseDrag', dragCallback);
$(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback);
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);
cornerstone.setViewport(eventData.element, eventData.viewport);
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
cornerstoneTools.pan = cornerstoneTools.simpleMouseButtonTool(mouseDownCallback);
cornerstoneTools.panTouchDrag = cornerstoneTools.touchDragTool(dragCallback);
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/pan.js
// Begin Source: src/imageTools/panMultiTouch.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function touchPanCallback(e, eventData) {
var config = cornerstoneTools.panMultiTouch.getConfiguration();
if (config && config.testPointers(eventData)) {
eventData.viewport.translation.x += (eventData.deltaPoints.page.x / eventData.viewport.scale);
eventData.viewport.translation.y += (eventData.deltaPoints.page.y / eventData.viewport.scale);
cornerstone.setViewport(eventData.element, eventData.viewport);
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
}
var configuration = {
testPointers: function(eventData) {
return (eventData.numPointers >= 2);
}
};
cornerstoneTools.panMultiTouch = cornerstoneTools.multiTouchDragTool(touchPanCallback);
cornerstoneTools.panMultiTouch.setConfiguration(configuration);
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/panMultiTouch.js
// Begin Source: src/imageTools/probe.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
var toolType = 'probe';
///////// BEGIN ACTIVE TOOL ///////
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: true
}
}
};
return measurementData;
}
///////// END ACTIVE TOOL ///////
///////// BEGIN IMAGE RENDERING ///////
function pointNearTool(element, data, coords) {
var endCanvas = cornerstone.pixelToCanvas(element, data.handles.end);
return cornerstoneMath.point.distance(endCanvas, coords) < 5;
}
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;
}
// 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);
var color;
var font = cornerstoneTools.textStyle.getFont();
var fontHeight = cornerstoneTools.textStyle.getFontSize();
for (var i = 0; i < toolData.data.length; i++) {
context.save();
var data = toolData.data[i];
if (data.active) {
color = cornerstoneTools.toolColors.getActiveColor();
} else {
color = cornerstoneTools.toolColors.getToolColor();
}
// draw the handles
cornerstoneTools.drawHandles(context, eventData, data.handles, color);
var x = Math.round(data.handles.end.x);
var y = Math.round(data.handles.end.y);
var storedPixels;
var text,
str;
if (x < 0 || y < 0 || x >= eventData.image.columns || y >= eventData.image.rows) {
return;
}
if (eventData.image.color) {
text = '' + x + ', ' + y;
storedPixels = cornerstoneTools.getRGBPixels(eventData.element, x, y, 1, 1);
str = 'R: ' + storedPixels[0] + ' G: ' + storedPixels[1] + ' B: ' + storedPixels[2];
} else {
storedPixels = cornerstone.getStoredPixels(eventData.element, x, y, 1, 1);
var sp = storedPixels[0];
var mo = sp * eventData.image.slope + eventData.image.intercept;
var suv = cornerstoneTools.calculateSUV(eventData.image, sp);
// Draw text
text = '' + x + ', ' + y;
str = 'SP: ' + sp + ' MO: ' + parseFloat(mo.toFixed(3));
if (suv) {
str += ' SUV: ' + parseFloat(suv.toFixed(3));
}
}
var coords = {
// translate the x/y away from the cursor
x: data.handles.end.x + 3,
y: data.handles.end.y - 3
};
var textCoords = cornerstone.pixelToCanvas(eventData.element, coords);
context.font = font;
context.fillStyle = color;
cornerstoneTools.drawTextBox(context, str, textCoords.x, textCoords.y + fontHeight + 5, color);
cornerstoneTools.drawTextBox(context, text, textCoords.x, textCoords.y, color);
context.restore();
}
}
///////// END IMAGE RENDERING ///////
// module exports
cornerstoneTools.probe = cornerstoneTools.mouseButtonTool({
createNewMeasurement: createNewMeasurement,
onImageRendered: onImageRendered,
pointNearTool: pointNearTool,
toolType: toolType
});
cornerstoneTools.probeTouch = cornerstoneTools.touchTool({
createNewMeasurement: createNewMeasurement,
onImageRendered: onImageRendered,
pointNearTool: pointNearTool,
toolType: toolType
});
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/probe.js
// Begin Source: src/imageTools/rectangleRoi.js
(function($, cornerstone, cornerstoneMath, cornerstoneTools) {
'use strict';
var toolType = 'rectangleRoi';
///////// BEGIN ACTIVE TOOL ///////
function createNewMeasurement(mouseEventData) {
// create the measurement data for this tool with the end handle activated
var measurementData = {
visible: true,
active: true,
handles: {
start: {
x: mouseEventData.currentPoints.image.x,
y: mouseEventData.currentPoints.image.y,
highlight: true,
active: false
},
end: {
x: mouseEventData.currentPoints.image.x,
y: mouseEventData.currentPoints.image.y,
highlight: true,
active: true
}
}
};
return measurementData;
}
///////// END ACTIVE TOOL ///////
function pointNearTool(element, data, coords) {
var startCanvas = cornerstone.pixelToCanvas(element, data.handles.start);
var endCanvas = cornerstone.pixelToCanvas(element, data.handles.end);
var rect = {
left: Math.min(startCanvas.x, endCanvas.x),
top: Math.min(startCanvas.y, endCanvas.y),
width: Math.abs(startCanvas.x - endCanvas.x),
height: Math.abs(startCanvas.y - endCanvas.y)
};
var distanceToPoint = cornerstoneMath.rect.distanceToPoint(rect, coords);
return (distanceToPoint < 5);
}
///////// BEGIN IMAGE RENDERING ///////
function calculateMeanStdDev(sp, ellipse) {
// TODO: Get a real statistics library here that supports large counts
var sum = 0;
var sumSquared = 0;
var count = 0;
var index = 0;
for (var y = ellipse.top; y < ellipse.top + ellipse.height; y++) {
for (var x = ellipse.left; x < ellipse.left + ellipse.width; x++) {
sum += sp[index];
sumSquared += sp[index] * sp[index];
count++;
index++;
}
}
if (count === 0) {
return {
count: count,
mean: 0.0,
variance: 0.0,
stdDev: 0.0
};
}
var mean = sum / count;
var variance = sumSquared / count - mean * mean;
return {
count: count,
mean: mean,
variance: variance,
stdDev: Math.sqrt(variance)
};
}
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 === undefined) {
return;
}
// 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
var color;
var lineWidth = cornerstoneTools.toolStyle.getToolWidth();
var font = cornerstoneTools.textStyle.getFont();
var fontHeight = cornerstoneTools.textStyle.getFontSize();
for (var i = 0; i < toolData.data.length; i++) {
context.save();
var data = toolData.data[i];
//differentiate the color of activation tool
if (data.active) {
color = cornerstoneTools.toolColors.getActiveColor();
} else {
color = cornerstoneTools.toolColors.getToolColor();
}
// draw the rectangle
var handleStartCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.start);
var handleEndCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.end);
var widthCanvas = Math.abs(handleStartCanvas.x - handleEndCanvas.x);
var heightCanvas = Math.abs(handleStartCanvas.y - handleEndCanvas.y);
var leftCanvas = Math.min(handleStartCanvas.x, handleEndCanvas.x);
var topCanvas = Math.min(handleStartCanvas.y, handleEndCanvas.y);
var centerX = (handleStartCanvas.x + handleEndCanvas.x) / 2;
var centerY = (handleStartCanvas.y + handleEndCanvas.y) / 2;
context.beginPath();
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.rect(leftCanvas, topCanvas, widthCanvas, heightCanvas);
context.stroke();
// draw the handles
cornerstoneTools.drawHandles(context, eventData, data.handles, color);
// Calculate the mean, stddev, and area
// TODO: calculate this in web worker for large pixel counts...
var width = Math.abs(data.handles.start.x - data.handles.end.x);
var height = Math.abs(data.handles.start.y - data.handles.end.y);
var left = Math.min(data.handles.start.x, data.handles.end.x);
var top = Math.min(data.handles.start.y, data.handles.end.y);
var pixels = cornerstone.getPixels(eventData.element, left, top, width, height);
var ellipse = {
left: left,
top: top,
width: width,
height: height
};
var meanStdDev = calculateMeanStdDev(pixels, ellipse);
var area = (width * eventData.image.columnPixelSpacing) * (height * eventData.image.rowPixelSpacing);
var areaText = 'Area: ' + area.toFixed(2) + ' mm^2';
// Draw text
context.font = font;
var textSize = context.measureText(area);
var textX = centerX < (eventData.image.columns / 2) ? centerX + (widthCanvas / 2): centerX - (widthCanvas / 2) - textSize.width;
var textY = centerY < (eventData.image.rows / 2) ? centerY + (heightCanvas / 2): centerY - (heightCanvas / 2);
context.fillStyle = color;
cornerstoneTools.drawTextBox(context, 'Mean: ' + meanStdDev.mean.toFixed(2), textX, textY - fontHeight - 5, color);
cornerstoneTools.drawTextBox(context, 'StdDev: ' + meanStdDev.stdDev.toFixed(2), textX, textY, color);
cornerstoneTools.drawTextBox(context, areaText, textX, textY + fontHeight + 5, color);
context.restore();
}
}
///////// END IMAGE RENDERING ///////
// module exports
cornerstoneTools.rectangleRoi = cornerstoneTools.mouseButtonTool({
createNewMeasurement: createNewMeasurement,
onImageRendered: onImageRendered,
pointNearTool: pointNearTool,
toolType: toolType
});
cornerstoneTools.rectangleRoiTouch = cornerstoneTools.touchTool({
createNewMeasurement: createNewMeasurement,
onImageRendered: onImageRendered,
pointNearTool: pointNearTool,
toolType: toolType
});
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/imageTools/rectangleRoi.js
// Begin Source: src/imageTools/rotate.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
// --- Strategies --- //
function defaultStrategy(eventData) {
// Calculate distance from the center of the image
var rect = eventData.element.getBoundingClientRect(eventData.element);
var points = {
x: eventData.currentPoints.client.x, y: eventData.currentPoints.client.y
};
var width = eventData.element.clientWidth;
var height = eventData.element.clientHeight;
var pointsFromCenter = {
x: points.x - rect.left - width / 2,
// Invert the coordinate system so that up is positive
y: -1 * (points.y - rect.top - height / 2)
};
var rotationRadians = Math.atan2(pointsFromCenter.y, pointsFromCenter.x);
var rotationDegrees = rotationRadians * (180 / Math.PI);
var rotation = -1 * rotationDegrees + 90;
eventData.viewport.rotation = rotation;
cornerstone.setViewport(eventData.element, eventData.viewport);
}
function horizontalStrategy(eventData) {
eventData.viewport.rotation += (eventData.deltaPoints.page.x / eventData.viewport.scale);
cornerstone.setViewport(eventData.element, eventData.viewport);
}
function verticalStrategy(eventData) {
eventData.viewport.rotation += (eventData.deltaPoints.page.y / eventData.viewport.scale);
cornerstone.setViewport(eventData.element, eventData.viewport);
}
// --- Mouse event callbacks --- //
function mouseUpCallback(e, eventData) {
$(eventData.element).off('CornerstoneToolsMouseDrag', dragCallback);
$(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback);
}
function mouseDownCallback(e, eventData) {
if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) {
$(eventData.element).on('CornerstoneToolsMouseDrag', dragCallback);
$(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback);
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
}
function dragCallback(e, eventData) {
cornerstoneTools.rotate.strategy(eventData);
cornerstone.setViewport(eventData.element, eventData.viewport);
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
cornerstoneTools.rotate = cornerstoneTools.simpleMouseButtonTool(mouseDownCallback);
cornerstoneTools.rotate.strategies = {
default: defaultStrategy,
horizontal: horizontalStrategy,
vertical: verticalStrategy
};
cornerstoneTools.rotate.strategy = defaultStrategy;
cornerstoneTools.rotateTouchDrag = cornerstoneTools.touchDragTool(dragCallback);
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/rotate.js
// Begin Source: src/imageTools/rotateTouch.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function touchRotateCallback(e, eventData) {
eventData.viewport.rotation += eventData.rotation;
cornerstone.setViewport(eventData.element, eventData.viewport);
return false;
}
function disable(element) {
$(element).off('CornerstoneToolsTouchRotate', touchRotateCallback);
}
function activate(element) {
$(element).off('CornerstoneToolsTouchRotate', touchRotateCallback);
$(element).on('CornerstoneToolsTouchRotate', touchRotateCallback);
}
cornerstoneTools.rotateTouch = {
activate: activate, disable: disable
};
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/rotateTouch.js
// Begin Source: src/imageTools/saveImage.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function saveAs(element, filename) {
var canvas = $(element).find('canvas').get(0);
// Thanks to Ken Fyrstenber
// http://stackoverflow.com/questions/18480474/how-to-save-an-image-from-canvas
var lnk = document.createElement('a');
/// the key here is to set the download attribute of the a tag
lnk.download = filename;
/// convert canvas content to data-uri for link. When download
/// attribute is set the content pointed to by link will be
/// pushed as 'download' in HTML5 capable browsers
lnk.href = canvas.toDataURL();
/// create a 'fake' click-event to trigger the download
if (document.createEvent) {
var e = document.createEvent('MouseEvents');
e.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
lnk.dispatchEvent(e);
} else if (lnk.fireEvent) {
lnk.fireEvent('onclick');
}
}
cornerstoneTools.saveAs = saveAs;
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/saveImage.js
// Begin Source: src/imageTools/simpleAngle.js
(function($, cornerstone, cornerstoneMath, cornerstoneTools) {
'use strict';
var toolType = 'simpleAngle';
///////// BEGIN ACTIVE TOOL ///////
function createNewMeasurement(mouseEventData) {
// create the measurement data for this tool with the end handle activated
var angleData = {
visible: true, active: true, handles: {
start: {
x: mouseEventData.currentPoints.image.x, y: mouseEventData.currentPoints.image.y, highlight: true, active: false
}, middle: {
x: mouseEventData.currentPoints.image.x, y: mouseEventData.currentPoints.image.y, highlight: true, active: true
}, end: {
x: mouseEventData.currentPoints.image.x, y: mouseEventData.currentPoints.image.y, highlight: true, active: false
}
}
};
return angleData;
}
///////// END ACTIVE TOOL ///////
function pointNearTool(element, data, coords) {
var lineSegment = {
start: cornerstone.pixelToCanvas(element, data.handles.start), end: cornerstone.pixelToCanvas(element, data.handles.middle)
};
var distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, coords);
if (distanceToPoint < 5) {
return true;
}
lineSegment.start = cornerstone.pixelToCanvas(element, data.handles.middle);
lineSegment.end = cornerstone.pixelToCanvas(element, data.handles.end);
distanceToPoint = cornerstoneMath.lineSegment.distanceToPoint(lineSegment, coords);
return (distanceToPoint < 5);
}
function length(vector) {
return Math.sqrt(Math.pow(vector.x, 2) + Math.pow(vector.y, 2));
}
///////// 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 === undefined) {
return;
}
// 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
var color;
var lineWidth = cornerstoneTools.toolStyle.getToolWidth();
var font = cornerstoneTools.textStyle.getFont();
for (var i = 0; i < toolData.data.length; i++) {
context.save();
var data = toolData.data[i];
//differentiate the color of activation tool
if (data.active) {
color = cornerstoneTools.toolColors.getActiveColor();
} else {
color = cornerstoneTools.toolColors.getToolColor();
}
// draw the line
context.beginPath();
context.strokeStyle = color;
context.lineWidth = lineWidth;
var handleStartCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.start);
var handleMiddleCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.middle);
context.moveTo(handleStartCanvas.x, handleStartCanvas.y);
context.lineTo(handleMiddleCanvas.x, handleMiddleCanvas.y);
var handleEndCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.end);
context.lineTo(handleEndCanvas.x, handleEndCanvas.y);
context.stroke();
// draw the handles
cornerstoneTools.drawHandles(context, eventData, data.handles, color);
// Draw the text
context.fillStyle = color;
// Default to isotropic pixel size, update suffix to reflect this
var columnPixelSpacing = eventData.image.columnPixelSpacing || 1;
var rowPixelSpacing = eventData.image.rowPixelSpacing || 1;
var suffix = '';
if (!eventData.image.rowPixelSpacing || !eventData.image.columnPixelSpacing) {
suffix = ' (isotropic)';
}
var sideA = {
x: (Math.ceil(data.handles.middle.x) - Math.ceil(data.handles.start.x)) * columnPixelSpacing, y: (Math.ceil(data.handles.middle.y) - Math.ceil(data.handles.start.y)) * rowPixelSpacing
};
var sideB = {
x: (Math.ceil(data.handles.end.x) - Math.ceil(data.handles.middle.x)) * columnPixelSpacing, y: (Math.ceil(data.handles.end.y) - Math.ceil(data.handles.middle.y)) * rowPixelSpacing
};
var sideC = {
x: (Math.ceil(data.handles.end.x) - Math.ceil(data.handles.start.x)) * columnPixelSpacing, y: (Math.ceil(data.handles.end.y) - Math.ceil(data.handles.start.y)) * rowPixelSpacing
};
var sideALength = length(sideA);
var sideBLength = length(sideB);
var sideCLength = length(sideC);
// Cosine law
var angle = Math.acos((Math.pow(sideALength, 2) + Math.pow(sideBLength, 2) - Math.pow(sideCLength, 2)) / (2 * sideALength * sideBLength));
angle = angle * (180 / Math.PI);
var rAngle = cornerstoneTools.roundToDecimal(angle, 2);
if (rAngle) {
var str = '00B0'; // degrees symbol
var text = rAngle.toString() + String.fromCharCode(parseInt(str, 16)) + suffix;
var distance = 15;
var textX = handleMiddleCanvas.x + distance;
var textY = handleMiddleCanvas.y + distance;
context.font = font;
var textWidth = context.measureText(text).width;
if ((handleMiddleCanvas.x - handleStartCanvas.x) < 0) {
textX = handleMiddleCanvas.x - distance - textWidth - 10;
} else {
textX = handleMiddleCanvas.x + distance;
}
textY = handleMiddleCanvas.y;
cornerstoneTools.drawTextBox(context, text, textX, textY, color);
}
context.restore();
}
}
///////// END IMAGE RENDERING ///////
///////// BEGIN ACTIVE TOOL ///////
function addNewMeasurement(mouseEventData) {
var measurementData = createNewMeasurement(mouseEventData);
var element = mouseEventData.element;
var eventData = {
mouseButtonMask: mouseEventData.which,
};
// associate this data with this imageId so we can render it and manipulate it
cornerstoneTools.addToolState(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.
$(element).off('CornerstoneToolsMouseMove', cornerstoneTools.simpleAngle.mouseMoveCallback);
$(element).off('CornerstoneToolsMouseDrag', cornerstoneTools.simpleAngle.mouseMoveCallback);
$(element).off('CornerstoneToolsMouseDown', cornerstoneTools.simpleAngle.mouseDownCallback);
$(element).off('CornerstoneToolsMouseDownActivate', cornerstoneTools.simpleAngle.mouseDownActivateCallback);
cornerstone.updateImage(element);
cornerstoneTools.moveNewHandle(mouseEventData, measurementData.handles.middle, function() {
measurementData.active = false;
if (cornerstoneTools.anyHandlesOutsideImage(mouseEventData, measurementData.handles)) {
// delete the measurement
cornerstoneTools.removeToolState(element, toolType, measurementData);
}
measurementData.handles.end.active = true;
cornerstone.updateImage(element);
cornerstoneTools.moveNewHandle(mouseEventData, measurementData.handles.end, function() {
measurementData.active = false;
if (cornerstoneTools.anyHandlesOutsideImage(mouseEventData, measurementData.handles)) {
// delete the measurement
cornerstoneTools.removeToolState(element, toolType, measurementData);
}
$(element).on('CornerstoneToolsMouseMove', cornerstoneTools.simpleAngle.mouseMoveCallback);
$(element).on('CornerstoneToolsMouseDrag', cornerstoneTools.simpleAngle.mouseMoveCallback);
$(element).on('CornerstoneToolsMouseDown', eventData, cornerstoneTools.simpleAngle.mouseDownCallback);
$(element).on('CornerstoneToolsMouseDownActivate', eventData, cornerstoneTools.simpleAngle.mouseDownActivateCallback);
cornerstone.updateImage(element);
});
});
}
function addNewMeasurementTouch(touchEventData) {
var measurementData = createNewMeasurement(touchEventData);
var element = touchEventData.element;
// associate this data with this imageId so we can render it and manipulate it
cornerstoneTools.addToolState(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.
$(element).off('CornerstoneToolsTouchDrag', cornerstoneTools.simpleAngleTouch.touchMoveCallback);
$(element).off('CornerstoneToolsDragStartActive', cornerstoneTools.simpleAngleTouch.touchDownActivateCallback);
$(element).off('CornerstoneToolsTap', cornerstoneTools.simpleAngleTouch.tapCallback);
cornerstone.updateImage(element);
cornerstoneTools.moveNewHandleTouch(touchEventData, measurementData.handles.middle, function() {
measurementData.active = false;
if (cornerstoneTools.anyHandlesOutsideImage(touchEventData, measurementData.handles)) {
// delete the measurement
cornerstoneTools.removeToolState(element, toolType, measurementData);
}
measurementData.handles.end.active = true;
cornerstone.updateImage(element);
cornerstoneTools.moveNewHandleTouch(touchEventData, measurementData.handles.end, function() {
measurementData.active = false;
if (cornerstoneTools.anyHandlesOutsideImage(touchEventData, measurementData.handles)) {
// delete the measurement
cornerstoneTools.removeToolState(element, toolType, measurementData);
}
$(element).on('CornerstoneToolsTouchDrag', cornerstoneTools.simpleAngleTouch.touchMoveCallback);
$(element).on('CornerstoneToolsDragStartActive', cornerstoneTools.simpleAngleTouch.touchDownActivateCallback);
$(element).on('CornerstoneToolsTap', cornerstoneTools.simpleAngleTouch.tapCallback);
cornerstone.updateImage(element);
});
});
}
cornerstoneTools.simpleAngle = cornerstoneTools.mouseButtonTool({
createNewMeasurement: createNewMeasurement,
addNewMeasurement: addNewMeasurement,
onImageRendered: onImageRendered,
pointNearTool: pointNearTool,
toolType: toolType
});
cornerstoneTools.simpleAngleTouch = cornerstoneTools.touchTool({
createNewMeasurement: createNewMeasurement,
addNewMeasurement: addNewMeasurementTouch,
onImageRendered: onImageRendered,
pointNearTool: pointNearTool,
toolType: toolType
});
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/imageTools/simpleAngle.js
// Begin Source: src/imageTools/textMarker.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
var toolType = 'textMarker';
///////// BEGIN ACTIVE TOOL ///////
function createNewMeasurement(mouseEventData) {
var config = cornerstoneTools.textMarker.getConfiguration();
if (!config.current) {
return;
}
// create the measurement data for this tool with the end handle activated
var measurementData = {
visible: true, active: true, text: config.current, handles: {
end: {
x: mouseEventData.currentPoints.image.x,
y: mouseEventData.currentPoints.image.y,
highlight: true,
active: true
}
}
};
// Update the current marker for the next marker
var currentIndex = config.markers.indexOf(config.current);
if (config.ascending) {
currentIndex += 1;
if (currentIndex >= config.markers.length) {
if (!config.loop) {
currentIndex = -1;
} else {
currentIndex -= config.markers.length;
}
}
} else {
currentIndex -= 1;
if (currentIndex < 0) {
if (!config.loop) {
currentIndex = -1;
} else {
currentIndex += config.markers.length;
}
}
}
config.current = config.markers[currentIndex];
return measurementData;
}
///////// END ACTIVE TOOL ///////
///////// BEGIN IMAGE RENDERING ///////
function pointNearTool(element, data, coords) {
var endCanvas = cornerstone.pixelToCanvas(element, data.handles.end);
var rect = {
left: endCanvas.x - data.textWidth / 2,
top: endCanvas.y,
width: data.textWidth,
height: data.textHeight
};
var distanceToPoint = cornerstoneMath.rect.distanceToPoint(rect, coords);
return (distanceToPoint < 10);
}
function onImageRendered(e, eventData) {
// 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) {
return;
}
// 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);
var font = cornerstoneTools.textStyle.getFont();
var fontSize = cornerstoneTools.textStyle.getFontSize();
for (var i = 0; i < toolData.data.length; i++) {
var data = toolData.data[i];
var color = cornerstoneTools.toolColors.getToolColor();
if (data.active) {
color = cornerstoneTools.toolColors.getActiveColor();
}
context.save();
// Draw text
context.font = font;
context.fillStyle = color;
var measureText = context.measureText(data.text);
data.textWidth = measureText.width;
data.textHeight = fontSize;
var coords = {
x: data.handles.end.x,
y: data.handles.end.y
};
var textCoords = cornerstone.pixelToCanvas(eventData.element, coords);
cornerstoneTools.drawTextBox(context, data.text, textCoords.x - data.textWidth / 2, textCoords.y, color);
context.restore();
}
}
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);
$(element).off('CornerstoneToolsMouseMove', cornerstoneTools.textMarker.mouseMoveCallback);
$(element).off('CornerstoneToolsMouseDown', cornerstoneTools.textMarker.mouseDownCallback);
$(element).off('CornerstoneToolsMouseDownActivate', cornerstoneTools.textMarker.mouseDownActivateCallback);
}
if (e.data && e.data.mouseButtonMask && !cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) {
return false;
}
var config = cornerstoneTools.textMarker.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)) {
data.active = true;
cornerstone.updateImage(element);
$(element).off('CornerstoneToolsMouseMove', cornerstoneTools.textMarker.mouseMoveCallback);
$(element).off('CornerstoneToolsMouseDown', cornerstoneTools.textMarker.mouseDownCallback);
$(element).off('CornerstoneToolsMouseDownActivate', cornerstoneTools.textMarker.mouseDownActivateCallback);
// Allow relabelling via a callback
config.changeTextCallback(data, doneChangingTextCallback);
e.stopImmediatePropagation();
return false;
}
}
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
function touchPressCallback(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);
$(element).on('CornerstoneToolsTouchDrag', cornerstoneTools.textMarkerTouch.touchMoveCallback);
$(element).on('CornerstoneToolsTouchStartActive', cornerstoneTools.textMarkerTouch.touchDownActivateCallback);
$(element).on('CornerstoneToolsTouchStart', cornerstoneTools.textMarkerTouch.touchStartCallback);
$(element).on('CornerstoneToolsTap', cornerstoneTools.textMarkerTouch.tapCallback);
$(element).on('CornerstoneToolsTouchPress', cornerstoneTools.textMarkerTouch.pressCallback);
}
if (e.data && e.data.mouseButtonMask && !cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) {
return false;
}
var config = cornerstoneTools.textMarker.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)) {
data.active = true;
cornerstone.updateImage(element);
$(element).off('CornerstoneToolsTouchDrag', cornerstoneTools.textMarkerTouch.touchMoveCallback);
$(element).off('CornerstoneToolsTouchStartActive', cornerstoneTools.textMarkerTouch.touchDownActivateCallback);
$(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(data, doneChangingTextCallback);
e.stopImmediatePropagation();
return false;
}
}
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
cornerstoneTools.textMarker = cornerstoneTools.mouseButtonTool({
createNewMeasurement: createNewMeasurement,
onImageRendered: onImageRendered,
pointNearTool: pointNearTool,
toolType: toolType,
mouseDoubleClickCallback: doubleClickCallback
});
cornerstoneTools.textMarkerTouch = cornerstoneTools.touchTool({
createNewMeasurement: createNewMeasurement,
onImageRendered: onImageRendered,
pointNearTool: pointNearTool,
toolType: toolType,
pressCallback: touchPressCallback
});
///////// END IMAGE RENDERING ///////
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/textMarker.js
// Begin Source: src/imageTools/wwwc.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function mouseUpCallback(e, eventData) {
$(eventData.element).off('CornerstoneToolsMouseDrag', mouseDragCallback);
$(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback);
}
function mouseDownCallback(e, eventData) {
if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) {
$(eventData.element).on('CornerstoneToolsMouseDrag', mouseDragCallback);
$(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback);
return false; // false = cases jquery to preventDefault() and stopPropagation() this event
}
}
function defaultStrategy(eventData) {
// here we normalize the ww/wc adjustments so the same number of on screen pixels
// adjusts the same percentage of the dynamic range of the image. This is needed to
// provide consistency for the ww/wc tool regardless of the dynamic range (e.g. an 8 bit
// image will feel the same as a 16 bit image would)
var maxVOI = eventData.image.maxPixelValue * eventData.image.slope + eventData.image.intercept;
var minVOI = eventData.image.minPixelValue * eventData.image.slope + eventData.image.intercept;
var imageDynamicRange = maxVOI - minVOI;
var multiplier = imageDynamicRange / 1024;
var deltaX = eventData.deltaPoints.page.x * multiplier;
var deltaY = eventData.deltaPoints.page.y * multiplier;
eventData.viewport.voi.windowWidth += (deltaX);
eventData.viewport.voi.windowCenter += (deltaY);
}
function mouseDragCallback(e, eventData) {
cornerstoneTools.wwwc.strategy(eventData);
cornerstone.setViewport(eventData.element, eventData.viewport);
return false; // false = cases jquery to preventDefault() and stopPropagation() this event
}
function touchDragCallback(e, eventData) {
var dragData = eventData;
var maxVOI = dragData.image.maxPixelValue * dragData.image.slope + dragData.image.intercept;
var minVOI = dragData.image.minPixelValue * dragData.image.slope + dragData.image.intercept;
var imageDynamicRange = maxVOI - minVOI;
var multiplier = imageDynamicRange / 1024;
var deltaX = dragData.deltaPoints.page.x * multiplier;
var deltaY = dragData.deltaPoints.page.y * multiplier;
var config = cornerstoneTools.wwwc.getConfiguration();
if (config.orientation) {
if (config.orientation === 0) {
dragData.viewport.voi.windowWidth += (deltaX);
dragData.viewport.voi.windowCenter += (deltaY);
} else {
dragData.viewport.voi.windowWidth += (deltaY);
dragData.viewport.voi.windowCenter += (deltaX);
}
} else {
dragData.viewport.voi.windowWidth += (deltaX);
dragData.viewport.voi.windowCenter += (deltaY);
}
cornerstone.setViewport(dragData.element, dragData.viewport);
}
cornerstoneTools.wwwc = cornerstoneTools.simpleMouseButtonTool(mouseDownCallback);
cornerstoneTools.wwwc.strategies = {
default: defaultStrategy
};
cornerstoneTools.wwwc.strategy = defaultStrategy;
cornerstoneTools.wwwcTouchDrag = cornerstoneTools.touchDragTool(touchDragCallback);
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/wwwc.js
// Begin Source: src/imageTools/wwwcRegion.js
(function($, cornerstone, cornerstoneMath, cornerstoneTools) {
'use strict';
var toolType = 'wwwcRegion';
var configuration = {
minWindowWidth: 10
};
/** Calculates the minimum, maximum, and mean value in the given pixel array */
function calculateMinMaxMean(storedPixelLuminanceData, globalMin, globalMax) {
var numPixels = storedPixelLuminanceData.length;
if (numPixels < 2) {
return {
min: globalMin,
max: globalMax,
mean: globalMin
};
}
var min = globalMax;
var max = globalMin;
var sum = 0;
for (var index = 0; index < numPixels; index++) {
var spv = storedPixelLuminanceData[index];
min = Math.min(min, spv);
max = Math.max(max, spv);
sum += spv;
}
return {
min: min,
max: max,
mean: sum / numPixels
};
}
/* Applies the windowing procedure when the mouse drag ends */
function dragEndCallback(e, eventData) {
$(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
};
$(eventData.element).on('CornerstoneToolsMouseDown', mouseData, mouseDownCallback);
}
/** Calculates the minimum and maximum value in the given pixel array */
function applyWWWCRegion(eventData) {
var toolData = cornerstoneTools.getToolState(eventData.element, toolType);
if (toolData === undefined) {
return;
}
if (!toolData.data.length) {
return;
}
var startPoint = toolData.data[0].startPoint;
var endPoint = toolData.data[0].endPoint;
// Get the rectangular region defined by the handles
var width = Math.abs(startPoint.x - endPoint.x);
var height = Math.abs(startPoint.y - endPoint.y);
var left = Math.min(startPoint.x, endPoint.x);
var top = Math.min(startPoint.y, endPoint.y);
// Bound the rectangle so we don't get undefined pixels
left = Math.max(left, 0);
left = Math.min(left, eventData.image.width);
top = Math.max(top, 0);
top = Math.min(top, eventData.image.height);
width = Math.floor(Math.min(width, Math.abs(eventData.image.width - left)));
height = Math.floor(Math.min(height, Math.abs(eventData.image.height - top)));
// Get the pixel data in the rectangular region
var pixelLuminanceData = cornerstoneTools.getLuminance(eventData.element, left, top, width, height);
// Calculate the minimum and maximum pixel values
var minMaxMean = calculateMinMaxMean(pixelLuminanceData, eventData.image.minPixelValue, eventData.image.maxPixelValue);
// Adjust the viewport window width and center based on the calculated values
var config = cornerstoneTools.wwwcRegion.getConfiguration();
var viewport = cornerstone.getViewport(eventData.element);
viewport.voi.windowWidth = Math.max(Math.abs(minMaxMean.max - minMaxMean.min), config.minWindowWidth);
viewport.voi.windowCenter = minMaxMean.mean;
cornerstone.setViewport(eventData.element, viewport);
// Clear the toolData
toolData.data = [];
cornerstone.updateImage(eventData.element);
}
function whichMovement(e, eventData) {
var element = eventData.element;
$(element).off('CornerstoneToolsMouseMove');
$(element).off('CornerstoneToolsMouseDrag');
$(element).on('CornerstoneToolsMouseMove', dragCallback);
$(element).on('CornerstoneToolsMouseDrag', dragCallback);
$(element).on('CornerstoneToolsMouseClick', dragEndCallback);
if (e.type === 'CornerstoneToolsMouseDrag') {
$(element).on('CornerstoneToolsMouseUp', dragEndCallback);
}
}
/** Records the start point and attaches the drag event handler */
function mouseDownCallback(e, eventData) {
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');
recordStartPoint(eventData);
return false;
}
}
/** Records the start point of the click or touch */
function recordStartPoint(eventData) {
var toolData = cornerstoneTools.getToolState(eventData.element, toolType);
if (toolData && toolData.data) {
toolData.data = [];
}
var measurementData = {
startPoint: {
x: eventData.currentPoints.image.x,
y: eventData.currentPoints.image.y
}
};
cornerstoneTools.addToolState(eventData.element, toolType, measurementData);
}
/** Draws the rectangular region while the touch or mouse event drag occurs */
function dragCallback(e, eventData) {
// 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) {
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;
cornerstone.updateImage(eventData.element);
}
function onImageRendered(e, eventData) {
var toolData = cornerstoneTools.getToolState(eventData.element, toolType);
if (toolData === undefined) {
return;
}
if (!toolData.data.length) {
return;
}
var startPoint = toolData.data[0].startPoint;
var endPoint = toolData.data[0].endPoint;
if (!startPoint || !endPoint) {
return;
}
// Get the current element's canvas
var canvas = $(eventData.element).find('canvas').get(0);
var context = canvas.getContext('2d');
context.setTransform(1, 0, 0, 1, 0, 0);
// 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);
var left = Math.min(startPointCanvas.x, endPointCanvas.x);
var top = Math.min(startPointCanvas.y, endPointCanvas.y);
var width = Math.abs(startPointCanvas.x - endPointCanvas.x);
var height = Math.abs(startPointCanvas.y - endPointCanvas.y);
var lineWidth = cornerstoneTools.toolStyle.getToolWidth();
// Draw the rectangle
context.save();
context.beginPath();
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.rect(left, top, width, height);
context.stroke();
context.restore();
}
// --- Mouse tool enable / disable --- ///
function disable(element) {
$(element).off('CornerstoneToolsMouseDown', mouseDownCallback);
$(element).off('CornerstoneImageRendered', onImageRendered);
cornerstone.updateImage(element);
}
function activate(element, mouseButtonMask) {
var eventData = {
mouseButtonMask: mouseButtonMask,
};
var toolData = cornerstoneTools.getToolState(element, toolType);
if (!toolData) {
var data = [];
cornerstoneTools.addToolState(element, toolType, data);
}
$(element).off('CornerstoneToolsMouseDown', mouseDownCallback);
$(element).off('CornerstoneToolsMouseUp', dragEndCallback);
$(element).off('CornerstoneToolsMouseDrag', dragCallback);
$(element).off('CornerstoneImageRendered', onImageRendered);
$(element).on('CornerstoneToolsMouseDown', eventData, mouseDownCallback);
$(element).on('CornerstoneImageRendered', onImageRendered);
cornerstone.updateImage(element);
}
// --- Touch tool enable / disable --- //
function disableTouchDrag(element) {
$(element).off('CornerstoneToolsTouchDrag', dragCallback);
$(element).off('CornerstoneToolsTouchStart', recordStartPoint);
$(element).off('CornerstoneToolsDragEnd', applyWWWCRegion);
$(element).off('CornerstoneImageRendered', onImageRendered);
}
function activateTouchDrag(element) {
var toolData = cornerstoneTools.getToolState(element, toolType);
if (toolData === undefined) {
var data = [];
cornerstoneTools.addToolState(element, toolType, data);
}
$(element).off('CornerstoneToolsTouchDrag', dragCallback);
$(element).off('CornerstoneToolsTouchStart', recordStartPoint);
$(element).off('CornerstoneToolsDragEnd', applyWWWCRegion);
$(element).off('CornerstoneImageRendered', onImageRendered);
$(element).on('CornerstoneToolsTouchDrag', dragCallback);
$(element).on('CornerstoneToolsTouchStart', recordStartPoint);
$(element).on('CornerstoneToolsDragEnd', applyWWWCRegion);
$(element).on('CornerstoneImageRendered', onImageRendered);
}
function getConfiguration() {
return configuration;
}
function setConfiguration(config) {
configuration = config;
}
// module exports
cornerstoneTools.wwwcRegion = {
activate: activate,
deactivate: disable,
disable: disable,
setConfiguration: setConfiguration,
getConfiguration: getConfiguration
};
cornerstoneTools.wwwcRegionTouch = {
activate: activateTouchDrag,
deactivate: disableTouchDrag,
disable: disableTouchDrag
};
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/imageTools/wwwcRegion.js
// Begin Source: src/imageTools/zoom.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
var startPoints;
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;
} else if (config.minScale && scale < config.minScale) {
viewport.scale = config.minScale;
} else {
viewport.scale = scale;
}
return viewport;
}
function boundPosition(position, width, height) {
position.x = Math.max(position.x, 0);
position.y = Math.max(position.y, 0);
position.x = Math.min(position.x, width);
position.y = Math.min(position.y, height);
return position;
}
function correctShift(shift, viewport) {
// 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;
shift.x = newX;
shift.y = newY;
}
// Apply Flips
if (viewport.hflip) {
shift.x *= -1;
}
if (viewport.vflip) {
shift.y *= -1;
}
return shift;
}
function defaultStrategy(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);
// 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);
var shift = {
x: eventData.startPoints.image.x - newCoords.x,
y: eventData.startPoints.image.y - newCoords.y
};
shift = correctShift(shift, viewport);
viewport.translation.x -= shift.x;
viewport.translation.y -= shift.y;
cornerstone.setViewport(element, viewport);
}
function translateStrategy(eventData, ticks) {
var element = eventData.element;
var image = eventData.image;
// Calculate the new scale factor based on how far the mouse has changed
var viewport = changeViewportScale(eventData.viewport, ticks);
cornerstone.setViewport(element, viewport);
var config = cornerstoneTools.zoom.getConfiguration();
var shift,
newCoords;
var outwardsTranslateSpeed = 8;
var inwardsTranslateSpeed = 8;
var outwardsMinScaleToTranslate = 3;
var minTranslation = 0.01;
if (ticks < 0) {
// Zoom outwards from the image center
shift = {
x: viewport.scale < outwardsMinScaleToTranslate ? viewport.translation.x / outwardsTranslateSpeed : 0,
y: viewport.scale < outwardsMinScaleToTranslate ? viewport.translation.y / outwardsTranslateSpeed : 0
};
if (Math.abs(viewport.translation.x) < minTranslation) {
viewport.translation.x = 0;
shift.x = 0;
} else if (Math.abs(viewport.translation.y) < minTranslation) {
viewport.translation.y = 0;
shift.y = 0;
} else if (Math.abs(viewport.translation.x) < minTranslation &&
Math.abs(viewport.translation.y) < minTranslation) {
cornerstone.setViewport(element, viewport);
return false;
}
} else {
newCoords = cornerstone.pageToPixel(element, startPoints.page.x, startPoints.page.y);
if (config && config.preventZoomOutsideImage) {
startPoints.image = boundPosition(startPoints.image, image.width, image.height);
newCoords = boundPosition(newCoords, image.width, image.height);
}
// Zoom inwards to the current image point
var desiredTranslation = {
x: image.width / 2 - startPoints.image.x,
y: image.height / 2 - startPoints.image.y
};
var distanceToDesired = {
x: viewport.translation.x - desiredTranslation.x,
y: viewport.translation.y - desiredTranslation.y
};
shift = {
x: distanceToDesired.x / inwardsTranslateSpeed,
y: distanceToDesired.y / inwardsTranslateSpeed
};
if (Math.abs(distanceToDesired.x) < minTranslation) {
viewport.translation.x = desiredTranslation.x;
shift.x = 0;
} else if (Math.abs(distanceToDesired.y) < minTranslation) {
viewport.translation.y = desiredTranslation.y;
shift.y = 0;
} else if (Math.abs(distanceToDesired.x) < minTranslation &&
Math.abs(distanceToDesired.y) < minTranslation) {
cornerstone.setViewport(element, viewport);
return false;
}
}
shift = correctShift(shift, viewport);
if (!shift.x && !shift.y) {
return false;
}
viewport.translation.x -= shift.x;
viewport.translation.y -= shift.y;
cornerstone.setViewport(element, viewport);
}
function mouseUpCallback(e, eventData) {
$(eventData.element).off('CornerstoneToolsMouseDrag', dragCallback);
$(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback);
}
function mouseDownCallback(e, eventData) {
if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) {
startPoints = eventData.startPoints; // Used for translateStrategy
$(eventData.element).on('CornerstoneToolsMouseDrag', dragCallback);
$(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback);
return false; // false = cases jquery to preventDefault() and stopPropagation() this event
}
}
function dragCallback(e, eventData) {
if (!eventData.deltaPoints.page.y) {
return false;
}
var ticks = eventData.deltaPoints.page.y / 100;
cornerstoneTools.zoom.strategy(eventData, ticks);
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
function mouseWheelCallback(e, eventData) {
var ticks = -eventData.direction / 4;
var viewport = changeViewportScale(eventData.viewport, ticks);
cornerstone.setViewport(eventData.element, viewport);
}
function touchPinchCallback(e, eventData) {
var config = cornerstoneTools.zoom.getConfiguration();
var viewport = eventData.viewport;
var element = eventData.element;
var scale = eventData.scaleChange;
// Change the scale based on the pinch gesture's scale change
if (config.maxScale && scale > config.maxScale) {
viewport.scale = config.maxScale;
} else if (config.minScale && scale < config.minScale) {
viewport.scale = config.minScale;
} else {
viewport.scale = scale;
}
cornerstone.setViewport(element, viewport);
// 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);
var shift = {
x: eventData.startPoints.image.x - newCoords.x,
y: eventData.startPoints.image.y - newCoords.y
};
shift = correctShift(shift, viewport);
viewport.translation.x -= shift.x;
viewport.translation.y -= shift.y;
cornerstone.setViewport(element, viewport);
}
cornerstoneTools.zoom = cornerstoneTools.simpleMouseButtonTool(mouseDownCallback);
cornerstoneTools.zoom.strategies = {
default: defaultStrategy,
translate: translateStrategy
};
cornerstoneTools.zoom.strategy = defaultStrategy;
cornerstoneTools.zoomWheel = cornerstoneTools.mouseWheelTool(mouseWheelCallback);
cornerstoneTools.zoomTouchPinch = cornerstoneTools.touchPinchTool(touchPinchCallback);
cornerstoneTools.zoomTouchDrag = cornerstoneTools.touchDragTool(dragCallback);
})($, cornerstone, cornerstoneTools);
// End Source; src/imageTools/zoom.js
// Begin Source: src/inputSources/keyboardInput.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
var mouseX, mouseY;
function keyPress(e) {
var element = e.currentTarget;
var startingCoords = cornerstone.pageToPixel(element, mouseX, mouseY);
e = window.event || e; // old IE support
var keyPressData = {
element: element, viewport: cornerstone.getViewport(element), image: cornerstone.getEnabledElement(element).image, pageX: mouseX, pageY: mouseY, imageX: startingCoords.x, imageY: startingCoords.y, keyCode: e.keyCode, which: e.which
};
if (e.type === 'keydown') {
$(element).trigger('CornerstoneToolsKeyDown', keyPressData);
} else if (e.type === 'keypress') {
$(element).trigger('CornerstoneToolsKeyPress', keyPressData);
} else if (e.type === 'keyup') {
$(element).trigger('CornerstoneToolsKeyUp', keyPressData);
}
}
function mouseMove(e) {
mouseX = e.pageX || e.originalEvent.pageX;
mouseY = e.pageY || e.originalEvent.pageY;
}
var keyboardEvent = 'keydown keypress keyup';
function enable(element) {
$(element).bind(keyboardEvent, keyPress);
$(element).on('mousemove', mouseMove);
}
function disable(element) {
$(element).unbind(keyboardEvent, keyPress);
}
// module exports
cornerstoneTools.keyboardInput = {
enable: enable, disable: disable
};
})($, cornerstone, cornerstoneTools);
// End Source; src/inputSources/keyboardInput.js
// Begin Source: src/inputSources/preventGhostClick.js
(function(cornerstoneTools) {
'use strict';
// Functions to prevent ghost clicks following a touch
// All credit to @kosich
// https://gist.github.com/kosich/23188dd86633b6c2efb7
var antiGhostDelay = 2000;
var pointerType = {
mouse: 0,
touch: 1
};
function preventGhostClick(element) {
var lastInteractionType,
lastInteractionTime;
function handleTap(type, e) {
var now = Date.now();
if (type !== lastInteractionType) {
if (now - lastInteractionTime <= antiGhostDelay) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
return false;
}
lastInteractionType = type;
}
lastInteractionTime = now;
}
function attachEvents(eventList, interactionType) {
eventList.forEach(function(eventName) {
element.addEventListener(eventName, handleTap.bind(null, interactionType), true);
});
}
var mouseEvents = [ 'mousedown', 'mouseup', 'mousemove' ];
var touchEvents = [ 'touchstart', 'touchend' ];
attachEvents(mouseEvents, pointerType.mouse);
attachEvents(touchEvents, pointerType.touch);
}
cornerstoneTools.preventGhostClick = preventGhostClick;
})(cornerstoneTools);
// End Source; src/inputSources/preventGhostClick.js
// Begin Source: src/manipulators/anyHandlesOutsideImage.js
(function($, cornerstone, cornerstoneMath, cornerstoneTools) {
'use strict';
function anyHandlesOutsideImage(renderData, handles) {
var image = renderData.image;
var imageRect = {
left: 0,
top: 0,
width: image.width,
height: image.height
};
var handleOutsideImage = false;
Object.keys(handles).forEach(function(name) {
var handle = handles[name];
if (cornerstoneMath.point.insideRect(handle, imageRect) === false) {
handleOutsideImage = true;
}
});
return handleOutsideImage;
}
// module/private exports
cornerstoneTools.anyHandlesOutsideImage = anyHandlesOutsideImage;
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/manipulators/anyHandlesOutsideImage.js
// Begin Source: src/manipulators/drawHandles.js
(function(cornerstone, cornerstoneTools) {
'use strict';
var handleRadius = 6;
function drawHandles(context, renderData, handles, color, fill) {
context.strokeStyle = color;
Object.keys(handles).forEach(function(name) {
var handle = handles[name];
context.beginPath();
if (handle.active) {
context.lineWidth = cornerstoneTools.toolStyle.getActiveWidth();
} else {
context.lineWidth = cornerstoneTools.toolStyle.getToolWidth();
}
var handleCanvasCoords = cornerstone.pixelToCanvas(renderData.element, handle);
context.arc(handleCanvasCoords.x, handleCanvasCoords.y, handleRadius, 0, 2 * Math.PI);
if (fill) {
context.fillStyle = fill;
context.fill();
}
context.stroke();
});
}
// module/private exports
cornerstoneTools.drawHandles = drawHandles;
})(cornerstone, cornerstoneTools);
// End Source; src/manipulators/drawHandles.js
// Begin Source: src/manipulators/getHandleNearImagePoint.js
(function($, cornerstone, cornerstoneMath, cornerstoneTools) {
'use strict';
function getHandleNearImagePoint(element, data, coords, distanceSq) {
var nearbyHandle;
if (!data.handles) {
return;
}
Object.keys(data.handles).forEach(function(name) {
var handle = data.handles[name];
var handleCanvas = cornerstone.pixelToCanvas(element, handle);
var distanceSquared = cornerstoneMath.point.distanceSquared(handleCanvas, coords);
if (distanceSquared < distanceSq) {
nearbyHandle = handle;
return;
}
});
return nearbyHandle;
}
// module exports
cornerstoneTools.getHandleNearImagePoint = getHandleNearImagePoint;
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/manipulators/getHandleNearImagePoint.js
// Begin Source: src/manipulators/handleActivator.js
(function($, cornerstone, cornerstoneMath, cornerstoneTools) {
'use strict';
function findHandleNear(element, handles, canvasPoint, distanceThreshold) {
var nearbyHandle;
Object.keys(handles).forEach(function(name) {
var handle = handles[name];
var handleCanvas = cornerstone.pixelToCanvas(element, handle);
var distance = cornerstoneMath.point.distance(handleCanvas, canvasPoint);
if (distance <= distanceThreshold) {
nearbyHandle = handle;
return;
}
});
return nearbyHandle;
}
function getActiveHandle(handles) {
var activeHandle;
Object.keys(handles).forEach(function(name) {
var handle = handles[name];
if (handle.active === true) {
activeHandle = handle;
return;
}
});
return activeHandle;
}
function handleActivator(element, handles, canvasPoint, distanceThreshold) {
if (!distanceThreshold) {
distanceThreshold = 36;
}
var activeHandle = getActiveHandle(handles);
var nearbyHandle = findHandleNear(element, handles, canvasPoint, distanceThreshold);
if (activeHandle !== nearbyHandle) {
if (nearbyHandle !== undefined) {
nearbyHandle.active = true;
}
if (activeHandle !== undefined) {
activeHandle.active = false;
}
return true;
}
return false;
}
// module/private exports
cornerstoneTools.handleActivator = handleActivator;
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/manipulators/handleActivator.js
// Begin Source: src/manipulators/moveAllHandles.js
(function($, cornerstone, cornerstoneMath, cornerstoneTools) {
'use strict';
function moveAllHandles(mouseEventData, data, toolData, options, doneMovingCallback) {
var element = mouseEventData.element;
function mouseDragCallback(e, eventData) {
data.active = true;
Object.keys(data.handles).forEach(function(name) {
var handle = data.handles[name];
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);
handle.y = Math.max(handle.y, 0);
handle.y = Math.min(handle.y, eventData.image.height);
}
});
cornerstone.updateImage(element);
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
$(element).on('CornerstoneToolsMouseDrag', mouseDragCallback);
function mouseUpCallback(e, eventData) {
data.active = false;
data.invalidated = true;
$(element).off('CornerstoneToolsMouseDrag', mouseDragCallback);
$(element).off('CornerstoneToolsMouseUp', mouseUpCallback);
$(element).off('CornerstoneToolsMouseClick', mouseUpCallback);
// If any handle is outside the image, delete the tool data
if (options.deleteIfHandleOutsideImage === true) {
var image = eventData.image;
var handleOutsideImage = false;
var rect = {
top: 0, left: 0, width: image.width, height: image.height
};
Object.keys(data.handles).forEach(function(name) {
var handle = data.handles[name];
handle.active = false;
if (cornerstoneMath.point.insideRect(handle, rect) === false) {
handleOutsideImage = true;
return;
}
});
if (handleOutsideImage) {
// find this tool data
var indexOfData = -1;
toolData.data.forEach(function(thisToolData, index) {
if (thisToolData === data) {
indexOfData = index;
return;
}
});
if (indexOfData !== -1) {
toolData.data.splice(indexOfData, 1);
}
}
}
cornerstone.updateImage(element);
doneMovingCallback();
}
$(element).on('CornerstoneToolsMouseUp', mouseUpCallback);
$(element).on('CornerstoneToolsMouseClick', mouseUpCallback);
return true;
}
// module/private exports
cornerstoneTools.moveAllHandles = moveAllHandles;
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/manipulators/moveAllHandles.js
// Begin Source: src/manipulators/moveHandle.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function moveHandle(mouseEventData, handle, doneMovingCallback, preventHandleOutsideImage) {
var element = mouseEventData.element;
function mouseDragCallback(e, eventData) {
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);
handle.y = Math.max(handle.y, 0);
handle.y = Math.min(handle.y, eventData.image.height);
}
cornerstone.updateImage(element);
}
$(element).on('CornerstoneToolsMouseDrag', mouseDragCallback);
function mouseUpCallback() {
handle.active = false;
$(element).off('CornerstoneToolsMouseDrag', mouseDragCallback);
$(element).off('CornerstoneToolsMouseUp', mouseUpCallback);
$(element).off('CornerstoneToolsMouseClick', mouseUpCallback);
cornerstone.updateImage(element);
doneMovingCallback();
}
$(element).on('CornerstoneToolsMouseUp', mouseUpCallback);
$(element).on('CornerstoneToolsMouseClick', mouseUpCallback);
}
// module/private exports
cornerstoneTools.moveHandle = moveHandle;
})($, cornerstone, cornerstoneTools);
// End Source; src/manipulators/moveHandle.js
// Begin Source: src/manipulators/moveNewHandle.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function moveNewHandle(mouseEventData, handle, doneMoveCallback, preventHandleOutsideImage) {
var element = mouseEventData.element;
function moveCallback(e, eventData) {
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);
handle.y = Math.max(handle.y, 0);
handle.y = Math.min(handle.y, eventData.image.height);
}
cornerstone.updateImage(element);
}
function whichMovement(e) {
$(element).off('CornerstoneToolsMouseMove');
$(element).off('CornerstoneToolsMouseDrag');
$(element).on('CornerstoneToolsMouseMove', moveCallback);
$(element).on('CornerstoneToolsMouseDrag', moveCallback);
$(element).on('CornerstoneToolsMouseClick', moveEndCallback);
if (e.type === 'CornerstoneToolsMouseDrag') {
$(element).on('CornerstoneToolsMouseUp', moveEndCallback);
}
}
$(element).on('CornerstoneToolsMouseDrag', whichMovement);
$(element).on('CornerstoneToolsMouseMove', whichMovement);
function moveEndCallback() {
$(element).off('CornerstoneToolsMouseMove', moveCallback);
$(element).off('CornerstoneToolsMouseDrag', moveCallback);
$(element).off('CornerstoneToolsMouseClick', moveEndCallback);
$(element).off('CornerstoneToolsMouseUp', moveEndCallback);
handle.active = false;
cornerstone.updateImage(element);
doneMoveCallback();
}
}
// module/private exports
cornerstoneTools.moveNewHandle = moveNewHandle;
})($, cornerstone, cornerstoneTools);
// End Source; src/manipulators/moveNewHandle.js
// Begin Source: src/manipulators/moveNewHandleTouch.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function moveNewHandleTouch(eventData, handle, doneMoveCallback, preventHandleOutsideImage) {
var element = eventData.element;
var distanceFromTouch = {
x: handle.x - eventData.currentPoints.image.x,
y: handle.y - eventData.currentPoints.image.y
};
function moveCallback(e, eventData) {
handle.active = true;
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);
handle.y = Math.max(handle.y, 0);
handle.y = Math.min(handle.y, eventData.image.height);
}
cornerstone.updateImage(element);
}
function moveEndCallback(e, eventData) {
$(element).off('CornerstoneToolsTouchDrag', moveCallback);
$(element).off('CornerstoneToolsTouchEnd', moveEndCallback);
$(element).off('CornerstoneToolsDragEnd', moveEndCallback);
$(element).off('CornerstoneToolsTap', moveEndCallback);
handle.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);
handle.y = Math.max(handle.y, 0);
handle.y = Math.min(handle.y, eventData.image.height);
}
cornerstone.updateImage(element);
doneMoveCallback();
}
$(element).on('CornerstoneToolsTouchDrag', moveCallback);
$(element).on('CornerstoneToolsTouchEnd', moveEndCallback);
$(element).on('CornerstoneToolsDragEnd', moveEndCallback);
$(element).on('CornerstoneToolsTap', moveEndCallback);
}
// module/private exports
cornerstoneTools.moveNewHandleTouch = moveNewHandleTouch;
})($, cornerstone, cornerstoneTools);
// End Source; src/manipulators/moveNewHandleTouch.js
// Begin Source: src/manipulators/touchMoveAllHandles.js
(function($, cornerstone, cornerstoneMath, cornerstoneTools) {
'use strict';
function touchMoveAllHandles(touchEventData, data, toolData, deleteIfHandleOutsideImage, doneMovingCallback) {
var element = touchEventData.element;
function touchDragCallback(e, eventData) {
data.active = true;
Object.keys(data.handles).forEach(function(name) {
var handle = data.handles[name];
handle.x += eventData.deltaPoints.image.x;
handle.y += eventData.deltaPoints.image.y;
});
cornerstone.updateImage(element);
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
$(element).on('CornerstoneToolsTouchDrag', touchDragCallback);
function touchEndCallback(e, eventData) {
data.active = false;
data.invalidated = false;
$(element).off('CornerstoneToolsTouchDrag', touchDragCallback);
$(element).off('CornerstoneToolsDragEnd', touchEndCallback);
$(element).off('CornerstoneToolsTap', touchEndCallback);
// If any handle is outside the image, delete the tool data
if (deleteIfHandleOutsideImage === true) {
var image = eventData.image;
var handleOutsideImage = false;
var rect = {
top: 0, left: 0, width: image.width, height: image.height
};
Object.keys(data.handles).forEach(function(name) {
var handle = data.handles[name];
if (cornerstoneMath.point.insideRect(handle, rect) === false) {
handleOutsideImage = true;
return;
}
});
if (handleOutsideImage) {
// find this tool data
var indexOfData = -1;
for (var i = 0; i < toolData.data.length; i++) {
if (toolData.data[i] === data) {
indexOfData = i;
}
}
if (indexOfData !== -1) {
toolData.data.splice(indexOfData, 1);
}
}
}
doneMovingCallback();
cornerstone.updateImage(element);
}
$(element).on('CornerstoneToolsDragEnd', touchEndCallback);
$(element).on('CornerstoneToolsTap', touchEndCallback);
return true;
}
// module/private exports
cornerstoneTools.touchMoveAllHandles = touchMoveAllHandles;
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/manipulators/touchMoveAllHandles.js
// Begin Source: src/manipulators/touchMoveHandle.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function touchMoveHandle(touchEventData, handle, doneMovingCallback) {
var element = touchEventData.element;
var distanceFromTouch = {
x: handle.x - touchEventData.currentPoints.image.x,
y: handle.y - touchEventData.currentPoints.image.y
};
function touchDragCallback(e, eventData) {
handle.active = true;
var touchMoveData = eventData;
handle.x = touchMoveData.currentPoints.image.x + distanceFromTouch.x;
handle.y = touchMoveData.currentPoints.image.y + distanceFromTouch.y;
cornerstone.updateImage(element);
}
$(element).on('CornerstoneToolsTouchDrag', touchDragCallback);
function touchEndCallback() {
handle.active = false;
$(element).off('CornerstoneToolsTouchDrag', touchDragCallback);
$(element).off('CornerstoneToolsTouchEnd', touchEndCallback);
$(element).off('CornerstoneToolsDragEnd', touchEndCallback);
$(element).off('CornerstoneToolsTap', touchEndCallback);
cornerstone.updateImage(element);
doneMovingCallback();
}
$(element).on('CornerstoneToolsTouchEnd', touchEndCallback);
$(element).on('CornerstoneToolsDragEnd', touchEndCallback);
$(element).on('CornerstoneToolsTap', touchEndCallback);
}
// module/private exports
cornerstoneTools.touchMoveHandle = touchMoveHandle;
})($, cornerstone, cornerstoneTools);
// End Source; src/manipulators/touchMoveHandle.js
// Begin Source: src/measurementManager/lineSample.js
(function($, cornerstoneTools) {
'use strict';
// This object manages a collection of measurements
function LineSampleMeasurement() {
var that = this;
that.samples = [];
// adds an element as both a source and a target
this.set = function(samples) {
that.samples = samples;
// fire event
$(that).trigger('CornerstoneLineSampleUpdated');
};
}
// module/private exports
cornerstoneTools.LineSampleMeasurement = LineSampleMeasurement;
})($, cornerstoneTools);
// End Source; src/measurementManager/lineSample.js
// Begin Source: src/measurementManager/measurementManager.js
(function($, cornerstoneTools) {
'use strict';
// This object manages a collection of measurements
function MeasurementManager() {
var that = this;
that.measurements = [];
// adds an element as both a source and a target
this.add = function(measurement) {
var index = that.measurements.push(measurement);
// fire event
var eventDetail = {
index: index, measurement: measurement
};
$(that).trigger('CornerstoneMeasurementAdded', eventDetail);
};
this.remove = function(index) {
var measurement = that.measurements[index];
that.measurements.splice(index, 1);
// fire event
var eventDetail = {
index: index, measurement: measurement
};
$(that).trigger('CornerstoneMeasurementRemoved', eventDetail);
};
}
// module/private exports
cornerstoneTools.MeasurementManager = new MeasurementManager();
})($, cornerstoneTools);
// End Source; src/measurementManager/measurementManager.js
// Begin Source: src/metaData.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
// this module defines a way for tools to access various metadata about an imageId. This layer of abstraction exists
// so metadata can be provided to the tools in different ways (e.g. by parsing DICOM P10 or by a WADO-RS document)
// NOTE: We may want to push this function down into the cornerstone core library, not sure yet...
var providers = [];
function addProvider( provider) {
providers.push(provider);
}
function removeProvider( provider) {
var index = providers.indexOf(provider);
if (index === -1) {
return;
}
providers.splice(index, 1);
}
function getMetaData(type, imageId) {
var result;
$.each(providers, function(index, provider) {
result = provider(type, imageId);
if (result !== undefined) {
return true;
}
});
return result;
}
// module/private exports
cornerstoneTools.metaData = {
addProvider: addProvider,
removeProvider: removeProvider,
get: getMetaData
};
})($, cornerstone, cornerstoneTools);
// End Source; src/metaData.js
// Begin Source: src/orientation/getOrientationString.js
(function(cornerstoneMath, cornerstoneTools) {
'use strict';
function getOrientationString(vector) {
// Thanks to David Clunie
// https://sites.google.com/site/dicomnotes/
var orientation = '',
orientationX = vector.x < 0 ? 'R' : 'L',
orientationY = vector.y < 0 ? 'A' : 'P',
orientationZ = vector.z < 0 ? 'F' : 'H';
// Should probably make this a function vector3.abs
var abs = new cornerstoneMath.Vector3(Math.abs(vector.x), Math.abs(vector.y), Math.abs(vector.z));
for (var i = 0; i < 3; i++) {
if (abs.x > 0.0001 && abs.x > abs.y && abs.x > abs.z) {
orientation += orientationX;
abs.x = 0;
} else if (abs.y > 0.0001 && abs.y > abs.x && abs.y > abs.z) {
orientation += orientationY;
abs.y = 0;
} else if (abs.z > 0.0001 && abs.z > abs.x && abs.z > abs.y) {
orientation += orientationZ;
abs.z = 0;
} else {
break;
}
}
return orientation;
}
// module/private exports
cornerstoneTools.orientation.getOrientationString = getOrientationString;
})(cornerstoneMath, cornerstoneTools);
// End Source; src/orientation/getOrientationString.js
// Begin Source: src/orientation/invertOrientationString.js
(function(cornerstoneTools) {
'use strict';
function invertOrientationString(string) {
var inverted = string.replace('H', 'f');
inverted = inverted.replace('F', 'h');
inverted = inverted.replace('R', 'l');
inverted = inverted.replace('L', 'r');
inverted = inverted.replace('A', 'p');
inverted = inverted.replace('P', 'a');
inverted = inverted.toUpperCase();
return inverted;
}
// module/private exports
cornerstoneTools.orientation.invertOrientationString = invertOrientationString;
})(cornerstoneTools);
// End Source; src/orientation/invertOrientationString.js
// Begin Source: src/referenceLines/calculateReferenceLine.js
(function(cornerstoneTools) {
'use strict';
// calculates a reference line between two planes by projecting the top left hand corner and bottom right hand corner
// of the reference image onto the target image. Ideally we would calculate the intersection between the planes but
// that requires a bit more math and this works fine for most cases
function calculateReferenceLine(targetImagePlane, referenceImagePlane) {
var points = cornerstoneTools.planePlaneIntersection(targetImagePlane, referenceImagePlane);
if (!points) {
return;
}
return {
start: cornerstoneTools.projectPatientPointToImagePlane(points.start, targetImagePlane),
end: cornerstoneTools.projectPatientPointToImagePlane(points.end, targetImagePlane)
};
}
// module/private exports
cornerstoneTools.referenceLines.calculateReferenceLine = calculateReferenceLine;
})(cornerstoneTools);
// End Source; src/referenceLines/calculateReferenceLine.js
// Begin Source: src/referenceLines/referenceLinesTool.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
var toolType = 'referenceLines';
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 === undefined) {
return;
}
// Get the enabled elements associated with this synchronization context and draw them
var syncContext = toolData.data[0].synchronizationContext;
var enabledElements = syncContext.getSourceElements();
var renderer = toolData.data[0].renderer;
// Create the canvas context and reset it to the pixel coordinate system
var context = eventData.canvasContext.canvas.getContext('2d');
cornerstone.setToPixelCoordinateSystem(eventData.enabledElement, context);
// Iterate over each referenced element
$.each(enabledElements, function(index, referenceEnabledElement) {
// don't draw ourselves
if (referenceEnabledElement === e.currentTarget) {
return;
}
// render it
renderer(context, eventData, e.currentTarget, referenceEnabledElement);
});
}
// enables the reference line tool for a given element. Note that a custom renderer
// can be provided if you want different rendering (e.g. all reference lines, first/last/active, etc)
function enable(element, synchronizationContext, renderer) {
renderer = renderer || cornerstoneTools.referenceLines.renderActiveReferenceLine;
cornerstoneTools.addToolState(element, toolType, {
synchronizationContext: synchronizationContext, renderer: renderer
});
$(element).on('CornerstoneImageRendered', onImageRendered);
cornerstone.updateImage(element);
}
// disables the reference line tool for the given element
function disable(element) {
$(element).off('CornerstoneImageRendered', onImageRendered);
cornerstone.updateImage(element);
}
// module/private exports
cornerstoneTools.referenceLines.tool = {
enable: enable, disable: disable
};
})($, cornerstone, cornerstoneTools);
// End Source; src/referenceLines/referenceLinesTool.js
// Begin Source: src/referenceLines/renderActiveReferenceLine.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
// renders the active reference line
function renderActiveReferenceLine(context, eventData, targetElement, referenceElement) {
var targetImage = cornerstone.getEnabledElement(targetElement).image;
var referenceImage = cornerstone.getEnabledElement(referenceElement).image;
// make sure the images are actually loaded for the target and reference
if (!targetImage || !referenceImage) {
return;
}
var targetImagePlane = cornerstoneTools.metaData.get('imagePlane', targetImage.imageId);
var referenceImagePlane = cornerstoneTools.metaData.get('imagePlane', referenceImage.imageId);
// the image planes must be in the same frame of reference
if (targetImagePlane.frameOfReferenceUID !== referenceImagePlane.frameOfReferenceUID) {
return;
}
// the image plane normals must be > 30 degrees apart
var targetNormal = targetImagePlane.rowCosines.clone().cross(targetImagePlane.columnCosines);
var referenceNormal = referenceImagePlane.rowCosines.clone().cross(referenceImagePlane.columnCosines);
var angleInRadians = targetNormal.angleTo(referenceNormal);
angleInRadians = Math.abs(angleInRadians);
if (angleInRadians < 0.5) { // 0.5 radians = ~30 degrees
return;
}
var referenceLine = cornerstoneTools.referenceLines.calculateReferenceLine(targetImagePlane, referenceImagePlane);
if (!referenceLine) {
return;
}
var refLineStartCanvas = cornerstone.pixelToCanvas(eventData.element, referenceLine.start);
var refLineEndCanvas = cornerstone.pixelToCanvas(eventData.element, referenceLine.end);
var color = cornerstoneTools.toolColors.getActiveColor();
var lineWidth = cornerstoneTools.toolStyle.getToolWidth();
// draw the referenceLines
context.setTransform(1, 0, 0, 1, 0, 0);
context.save();
context.beginPath();
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.moveTo(refLineStartCanvas.x, refLineStartCanvas.y);
context.lineTo(refLineEndCanvas.x, refLineEndCanvas.y);
context.stroke();
context.restore();
}
// module/private exports
cornerstoneTools.referenceLines.renderActiveReferenceLine = renderActiveReferenceLine;
})($, cornerstone, cornerstoneTools);
// End Source; src/referenceLines/renderActiveReferenceLine.js
// Begin Source: src/requestPool/requestPoolManager.js
(function(cornerstone, cornerstoneTools) {
'use strict';
var requestPool = {
interaction: [],
thumbnail: [],
prefetch: []
};
var numRequests = {
interaction: 0,
thumbnail: 0,
prefetch: 0
};
var maxNumRequests = {
interaction: 6,
thumbnail: 6,
prefetch: 5
};
var lastElementInteracted;
var awake = false;
var grabDelay = 20;
function requestPoolManager() {
function addRequest(element, imageId, type, doneCallback, failCallback) {
if (!requestPool.hasOwnProperty(type)) {
throw 'Request type must be one of interaction, thumbnail, or prefetch';
}
if (!element || !imageId) {
return;
}
// Describe the request
var requestDetails = {
type: type,
imageId: imageId,
doneCallback: doneCallback,
failCallback: failCallback
};
// If this imageId is in the cache, resolve it immediately
var imagePromise = cornerstone.imageCache.getImagePromise(imageId);
if (imagePromise) {
imagePromise.then(function(image) {
doneCallback(image);
}, function(error) {
failCallback(error);
});
return;
}
// Add it to the end of the stack
requestPool[type].push(requestDetails);
// Store the last element interacted with,
// So we know which images to prefetch
//
// ---- Not used for now ----
if (type === 'interaction') {
lastElementInteracted = element;
}
}
function clearRequestStack(type) {
//console.log('clearRequestStack');
if (!requestPool.hasOwnProperty(type)) {
throw 'Request type must be one of interaction, thumbnail, or prefetch';
}
requestPool[type] = [];
}
function startAgain() {
if (!awake) {
return;
}
setTimeout(function() {
var requestDetails = getNextRequest();
if (!requestDetails) {
awake = false;
return;
}
sendRequest(requestDetails);
}, grabDelay);
}
function sendRequest(requestDetails) {
// Increment the number of current requests of this type
var type = requestDetails.type;
numRequests[type]++;
awake = true;
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) {
// If we do, remove from list (when resolved, as we could have
// pending prefetch requests) and stop processing this iteration
imagePromise.then(function(image) {
numRequests[type]--;
// console.log(numRequests);
doneCallback(image);
startAgain();
}, function(error) {
numRequests[type]--;
// console.log(numRequests);
failCallback(error);
});
return;
}
// Load and cache the image
cornerstone.loadAndCacheImage(imageId).then(function(image) {
numRequests[type]--;
// console.log(numRequests);
doneCallback(image);
startAgain();
}, function(error) {
numRequests[type]--;
// console.log(numRequests);
failCallback(error);
});
}
function startGrabbing() {
// Begin by grabbing X images
if (awake) {
return;
}
var maxSimultaneousRequests = cornerstoneTools.getMaxSimultaneousRequests();
maxNumRequests = {
interaction: maxSimultaneousRequests,
thumbnail: maxSimultaneousRequests - 2,
prefetch: maxSimultaneousRequests - 1
};
for (var i = 0; i < maxSimultaneousRequests; i++) {
var requestDetails = getNextRequest();
if (requestDetails) {
sendRequest(requestDetails);
}
}
//console.log("startGrabbing");
//console.log(requestPool);
}
function getNextRequest() {
if (requestPool.interaction.length && numRequests.interaction < maxNumRequests.interaction) {
return requestPool.interaction.shift();
}
if (requestPool.thumbnail.length && numRequests.thumbnail < maxNumRequests.thumbnail) {
return requestPool.thumbnail.shift();
}
if (requestPool.prefetch.length && numRequests.prefetch < maxNumRequests.prefetch) {
return requestPool.prefetch.shift();
}
if (!requestPool.interaction.length &&
!requestPool.thumbnail.length &&
!requestPool.prefetch.length) {
awake = false;
}
return false;
}
function getRequestPool() {
return requestPool;
}
var requestManager = {
addRequest: addRequest,
clearRequestStack: clearRequestStack,
startGrabbing: startGrabbing,
getRequestPool: getRequestPool
};
return requestManager;
}
// module/private exports
cornerstoneTools.requestPoolManager = requestPoolManager();
})(cornerstone, cornerstoneTools);
// End Source; src/requestPool/requestPoolManager.js
// Begin Source: src/stackTools/playClip.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
var toolType = 'playClip';
/**
* Starts playing a clip or adjusts the frame rate of an already playing clip. framesPerSecond is
* optional and defaults to 30 if not specified. A negative framesPerSecond will play the clip in reverse.
* The element must be a stack of images
* @param element
* @param framesPerSecond
*/
function playClip(element, framesPerSecond) {
if (element === undefined) {
throw 'playClip: element must not be undefined';
}
if (framesPerSecond === undefined) {
framesPerSecond = 30;
}
var stackToolData = cornerstoneTools.getToolState(element, 'stack');
if (stackToolData === undefined || stackToolData.data === undefined || stackToolData.data.length === 0) {
return;
}
var stackData = stackToolData.data[0];
var playClipToolData = cornerstoneTools.getToolState(element, toolType);
var playClipData;
if (playClipToolData === undefined || playClipToolData.data.length === 0) {
playClipData = {
intervalId: undefined, framesPerSecond: framesPerSecond, lastFrameTimeStamp: undefined, frameRate: 0, loop: true
};
cornerstoneTools.addToolState(element, toolType, playClipData);
} else {
playClipData = playClipToolData.data[0];
playClipData.framesPerSecond = framesPerSecond;
}
// if already playing, do not set a new interval
if (playClipData.intervalId !== undefined) {
return;
}
playClipData.intervalId = setInterval(function() {
var newImageIdIndex = stackData.currentImageIdIndex;
if (playClipData.framesPerSecond > 0) {
newImageIdIndex++;
} else {
newImageIdIndex--;
}
if (!playClipData.loop && (newImageIdIndex >= stackData.imageIds.length || newImageIdIndex < 0)) {
var eventDetail = {
element: element
};
var event = $.Event('CornerstoneToolsClipStopped', eventDetail);
$(element).trigger(event, eventDetail);
clearInterval(playClipData.intervalId);
playClipData.intervalId = undefined;
return;
}
// loop around if we go outside the stack
if (newImageIdIndex >= stackData.imageIds.length) {
newImageIdIndex = 0;
}
if (newImageIdIndex < 0) {
newImageIdIndex = stackData.imageIds.length - 1;
}
if (newImageIdIndex !== stackData.currentImageIdIndex) {
var startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler();
var endLoadingHandler = cornerstoneTools.loadHandlerManager.getEndLoadHandler();
var errorLoadingHandler = cornerstoneTools.loadHandlerManager.getErrorLoadingHandler();
if (startLoadingHandler) {
startLoadingHandler(element);
}
var viewport = cornerstone.getViewport(element);
cornerstone.loadAndCacheImage(stackData.imageIds[newImageIdIndex]).then(function(image) {
stackData.currentImageIdIndex = newImageIdIndex;
cornerstone.displayImage(element, image, viewport);
if (endLoadingHandler) {
endLoadingHandler(element);
}
}, function(error) {
var imageId = stackData.imageIds[newImageIdIndex];
if (errorLoadingHandler) {
errorLoadingHandler(element, imageId, error);
}
});
}
}, 1000 / Math.abs(playClipData.framesPerSecond));
}
/**
* Stops an already playing clip.
* * @param element
*/
function stopClip(element) {
var playClipToolData = cornerstoneTools.getToolState(element, toolType);
var playClipData;
if (playClipToolData === undefined || playClipToolData.data.length === 0) {
return;
} else {
playClipData = playClipToolData.data[0];
}
clearInterval(playClipData.intervalId);
playClipData.intervalId = undefined;
}
// module/private exports
cornerstoneTools.playClip = playClip;
cornerstoneTools.stopClip = stopClip;
})($, cornerstone, cornerstoneTools);
// End Source; src/stackTools/playClip.js
// Begin Source: src/stackTools/scrollIndicator.js
/*
Display scroll progress bar across bottom of image.
*/
(function($, cornerstone, cornerstoneTools) {
'use strict';
var scrollBarHeight = 6;
var configuration = {
backgroundColor: 'rgb(19, 63, 141)',
fillColor: 'white'
};
function onImageRendered(e, eventData){
var element = eventData.element;
var width = eventData.enabledElement.canvas.width;
var height = eventData.enabledElement.canvas.height;
if (!width || !height) {
return false;
}
var context = eventData.enabledElement.canvas.getContext('2d');
context.setTransform(1, 0, 0, 1, 0, 0);
context.save();
var config = cornerstoneTools.scrollIndicator.getConfiguration();
// draw indicator background
context.fillStyle = config.backgroundColor;
context.fillRect(0, height - scrollBarHeight, width, scrollBarHeight);
// get current image index
var stackData = cornerstoneTools.getToolState(element, 'stack');
if (!stackData || !stackData.data || !stackData.data.length) {
return;
}
var imageIds = stackData.data[0].imageIds;
var currentImageIdIndex = stackData.data[0].currentImageIdIndex;
// draw current image cursor
var cursorWidth = width / imageIds.length;
var xPosition = cursorWidth * currentImageIdIndex;
context.fillStyle = config.fillColor;
context.fillRect(xPosition, height - scrollBarHeight, cursorWidth, scrollBarHeight);
context.restore();
}
cornerstoneTools.scrollIndicator = cornerstoneTools.displayTool(onImageRendered);
cornerstoneTools.scrollIndicator.setConfiguration(configuration);
})($, cornerstone, cornerstoneTools);
// End Source; src/stackTools/scrollIndicator.js
// Begin Source: src/stackTools/stackPrefetch.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
var toolType = 'stackPrefetch';
var requestType = 'prefetch';
var configuration = {};
var resetPrefetchTimeout,
resetPrefetchDelay;
function sortNumber(a, b) {
// http://stackoverflow.com/questions/1063007/how-to-sort-an-array-of-integers-correctly
return a - b;
}
function range(lowEnd, highEnd) {
// Javascript version of Python's range function
// http://stackoverflow.com/questions/3895478/does-javascript-have-a-method-like-range-to-generate-an-array-based-on-suppl
lowEnd = Math.round(lowEnd) || 0;
highEnd = Math.round(highEnd) || 0;
var arr = [],
c = highEnd - lowEnd + 1;
if (c <= 0) {
return arr;
}
while ( c-- ) {
arr[c] = highEnd--;
}
return arr;
}
var max = function(arr) {
return Math.max.apply(null, arr);
};
var min = function(arr) {
return Math.min.apply(null, arr);
};
function nearestIndex(arr, x) {
// Return index of nearest values in array
// http://stackoverflow.com/questions/25854212/return-index-of-nearest-values-in-an-array
var l = [],
h = [];
arr.forEach(function(v) {
if (v < x) {
l.push(v);
} else if (v > x) {
h.push(v);
}
});
return {
low: arr.indexOf(max(l)),
high: arr.indexOf(min(h))
};
}
function prefetch(element) {
// Check to make sure stack data exists
var stackData = cornerstoneTools.getToolState(element, 'stack');
if (!stackData || !stackData.data || !stackData.data.length) {
return;
}
var stack = stackData.data[0];
// Get the stackPrefetch tool data
var stackPrefetchData = cornerstoneTools.getToolState(element, toolType);
if (!stackPrefetchData) {
return;
}
var stackPrefetch = stackPrefetchData.data[0];
// If all the requests are complete, disable the stackPrefetch tool
if (!stackPrefetch || !stackPrefetch.indicesToRequest || !stackPrefetch.indicesToRequest.length) {
stackPrefetch.enabled = false;
}
// Make sure the tool is still enabled
if (stackPrefetch.enabled === false) {
return;
}
// Remove an imageIdIndex from the list of indices to request
// 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);
var indicesToRequestCopy = stackPrefetch.indicesToRequest.slice();
indicesToRequestCopy.forEach(function(imageIdIndex) {
var imageId = stack.imageIds[imageIdIndex];
if (!imageId) {
return;
}
var imagePromise = cornerstone.imageCache.getImagePromise(imageId);
if (imagePromise && imagePromise.state() === 'resolved'){
removeFromList(imageIdIndex);
}
});
// Stop here if there are no images left to request
// After those in the cache have been removed
if (!stackPrefetch.indicesToRequest.length) {
return;
}
function doneCallback(image) {
//console.log('prefetch done: ' + image.imageId);
var imageIdIndex = stack.imageIds.indexOf(image.imageId);
removeFromList(imageIdIndex);
}
function failCallback(error) {
console.log('prefetch errored: ' + error);
}
// Clear the requestPool of prefetch requests
var requestPoolManager = cornerstoneTools.requestPoolManager;
requestPoolManager.clearRequestStack(requestType);
// Identify the nearest imageIdIndex to the currentImageIdIndex
var nearest = nearestIndex(stackPrefetch.indicesToRequest, stack.currentImageIdIndex);
var imageId,
nextImageIdIndex;
// Prefetch images around the current image (before and after)
var lowerIndex = nearest.low;
var higherIndex = nearest.high;
while (lowerIndex > 0 || higherIndex < stackPrefetch.indicesToRequest.length) {
if (lowerIndex >= 0 ) {
nextImageIdIndex = stackPrefetch.indicesToRequest[lowerIndex--];
imageId = stack.imageIds[nextImageIdIndex];
requestPoolManager.addRequest(element, imageId, requestType, doneCallback, failCallback);
}
if (higherIndex < stackPrefetch.indicesToRequest.length) {
nextImageIdIndex = stackPrefetch.indicesToRequest[higherIndex++];
imageId = stack.imageIds[nextImageIdIndex];
requestPoolManager.addRequest(element, imageId, requestType, doneCallback, failCallback);
}
}
// Try to start the requestPool's grabbing procedure
// in case it isn't already running
requestPoolManager.startGrabbing();
}
function handleCacheFull(e) {
// Stop prefetching if the ImageCacheFull event is fired from cornerstone
// console.log('CornerstoneImageCacheFull full, stopping');
var element = e.data.element;
var stackPrefetchData = cornerstoneTools.getToolState(element, toolType);
if (!stackPrefetchData || !stackPrefetchData.data || !stackPrefetchData.data.length) {
return;
}
// Disable the stackPrefetch tool
// stackPrefetchData.data[0].enabled = false;
// Clear current prefetch requests from the requestPool
cornerstoneTools.requestPoolManager.clearRequestStack(requestType);
}
function promiseRemovedHandler(e, eventData) {
// When an imagePromise has been pushed out of the cache, re-add its index
// it to the indicesToRequest list so that it will be retrieved later if the
// currentImageIdIndex is changed to an image nearby
var element = e.data.element;
var stackData = cornerstoneTools.getToolState(element, 'stack');
if (!stackData || !stackData.data || !stackData.data.length) {
return;
}
var stack = stackData.data[0];
var imageIdIndex = stack.imageIds.indexOf(eventData.imageId);
// Make sure the image that was removed is actually in this stack
// before adding it to the indicesToRequest array
if (imageIdIndex < 0) {
return;
}
var stackPrefetchData = cornerstoneTools.getToolState(element, toolType);
if (!stackPrefetchData || !stackPrefetchData.data || !stackPrefetchData.data.length) {
return;
}
stackPrefetchData.data[0].indicesToRequest.push(imageIdIndex);
}
function onImageUpdated(e) {
// Start prefetching again (after a delay)
// When the user has scrolled to a new image
clearTimeout(resetPrefetchTimeout);
resetPrefetchTimeout = setTimeout(function() {
var element = e.currentTarget;
prefetch(element);
}, resetPrefetchDelay);
}
function enable(element) {
// Clear old prefetch data. Skipping this can cause problems when changing the series inside an element
var stackPrefetchDataArray = cornerstoneTools.getToolState(element, toolType);
stackPrefetchDataArray.data = [];
// First check that there is stack data available
var stackData = cornerstoneTools.getToolState(element, 'stack');
if (!stackData || !stackData.data || !stackData.data.length) {
return;
}
var stack = stackData.data[0];
// Use the currentImageIdIndex from the stack as the initalImageIdIndex
var stackPrefetchData = {
indicesToRequest: range(0, stack.imageIds.length - 1),
enabled: true,
direction: 1
};
// Remove the currentImageIdIndex from the list to request
var indexOfCurrentImage = stackPrefetchData.indicesToRequest.indexOf(stack.currentImageIdIndex);
stackPrefetchData.indicesToRequest.splice(indexOfCurrentImage, 1);
cornerstoneTools.addToolState(element, toolType, stackPrefetchData);
prefetch(element);
$(element).off('CornerstoneNewImage', onImageUpdated);
$(element).on('CornerstoneNewImage', onImageUpdated);
$(cornerstone).off('CornerstoneImageCacheFull', handleCacheFull);
$(cornerstone).on('CornerstoneImageCacheFull', {
element: element
}, handleCacheFull);
$(cornerstone).off('CornerstoneImageCachePromiseRemoved', promiseRemovedHandler);
$(cornerstone).on('CornerstoneImageCachePromiseRemoved', {
element: element
}, promiseRemovedHandler);
}
function disable(element) {
$(element).off('CornerstoneNewImage', onImageUpdated);
$(cornerstone).off('CornerstoneImageCacheFull', handleCacheFull);
$(cornerstone).off('CornerstoneImageCachePromiseRemoved', promiseRemovedHandler);
var stackPrefetchData = cornerstoneTools.getToolState(element, toolType);
// If there is actually something to disable, disable it
if (stackPrefetchData && stackPrefetchData.data.length) {
stackPrefetchData.data[0].enabled = false;
// Clear current prefetch requests from the requestPool
cornerstoneTools.requestPoolManager.clearRequestStack(requestType);
}
}
function getConfiguration () {
return configuration;
}
function setConfiguration(config) {
configuration = config;
}
// module/private exports
cornerstoneTools.stackPrefetch = {
enable: enable,
disable: disable,
getConfiguration: getConfiguration,
setConfiguration: setConfiguration
};
})($, cornerstone, cornerstoneTools);
// End Source; src/stackTools/stackPrefetch.js
// Begin Source: src/stackTools/stackScroll.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function mouseUpCallback(e, eventData) {
$(eventData.element).off('CornerstoneToolsMouseDrag', dragCallback);
$(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback);
}
function mouseDownCallback(e, eventData) {
if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) {
var mouseDragEventData = {
deltaY: 0
};
$(eventData.element).on('CornerstoneToolsMouseDrag', mouseDragEventData, dragCallback);
$(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback);
e.stopImmediatePropagation();
return false;
}
}
function mouseWheelCallback(e, eventData) {
var images = -eventData.direction;
cornerstoneTools.scroll(eventData.element, images);
}
function dragCallback(e, eventData) {
var element = eventData.element;
var toolData = cornerstoneTools.getToolState(element, 'stack');
if (!toolData || !toolData.data || !toolData.data.length) {
return;
}
var stackData = toolData.data[0];
var config = cornerstoneTools.stackScroll.getConfiguration();
// The Math.max here makes it easier to mouseDrag-scroll small image stacks
var pixelsPerImage = $(element).height() / Math.max(stackData.imageIds.length, 8);
if (config && config.stackScrollSpeed) {
pixelsPerImage = config.stackScrollSpeed;
}
e.data.deltaY = e.data.deltaY || 0;
e.data.deltaY += eventData.deltaPoints.page.y;
if (Math.abs(e.data.deltaY) >= pixelsPerImage) {
var imageDelta = e.data.deltaY / pixelsPerImage;
var imageIdIndexOffset = Math.round(imageDelta);
var imageDeltaMod = e.data.deltaY % pixelsPerImage;
e.data.deltaY = imageDeltaMod;
cornerstoneTools.scroll(element, imageIdIndexOffset);
}
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
// module/private exports
cornerstoneTools.stackScroll = cornerstoneTools.simpleMouseButtonTool(mouseDownCallback);
cornerstoneTools.stackScrollWheel = cornerstoneTools.mouseWheelTool(mouseWheelCallback);
var options = {
eventData: {
deltaY: 0
}
};
cornerstoneTools.stackScrollTouchDrag = cornerstoneTools.touchDragTool(dragCallback, options);
})($, cornerstone, cornerstoneTools);
// End Source; src/stackTools/stackScroll.js
// Begin Source: src/stackTools/stackScrollKeyboard.js
(function(cornerstoneTools) {
'use strict';
var keys = {
UP: 38, DOWN: 40
};
function keyDownCallback(e, eventData) {
var keyCode = eventData.keyCode;
if (keyCode !== keys.UP && keyCode !== keys.DOWN) {
return;
}
var images = 1;
if (keyCode === keys.DOWN) {
images = -1;
}
cornerstoneTools.scroll(eventData.element, images);
}
// module/private exports
cornerstoneTools.stackScrollKeyboard = cornerstoneTools.keyboardTool(keyDownCallback);
})(cornerstoneTools);
// End Source; src/stackTools/stackScrollKeyboard.js
// Begin Source: src/stateManagement/applicationState.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function saveApplicationState(elements) {
// Save imageId-specific tool state data
var appState = {
imageIdToolState: cornerstoneTools.globalImageIdSpecificToolStateManager.saveToolState(),
elementToolState: {},
elementViewport: {}
};
// For each of the given elements, save the viewport and any stack-specific tool data
elements.forEach(function(element) {
var toolStateManager = cornerstoneTools.getElementToolStateManager(element);
if (toolStateManager === cornerstoneTools.globalImageIdSpecificToolStateManager) {
return;
}
appState.elementToolState[element.id] = toolStateManager.saveToolState();
appState.elementViewport[element.id] = cornerstone.getViewport(element);
});
return appState;
}
function restoreApplicationState(appState) {
// Make sure t
if (!appState.hasOwnProperty('imageIdToolState') ||
!appState.hasOwnProperty('elementToolState') ||
!appState.hasOwnProperty('elementViewport')) {
return;
}
// Restore all the imageId specific tool data
cornerstoneTools.globalImageIdSpecificToolStateManager.restoreToolState(appState.imageIdToolState);
Object.keys(appState.elementViewport).forEach(function(elementId) {
// Restore any stack specific tool data
var element = document.getElementById(elementId);
if (!element) {
return;
}
if (!appState.elementToolState.hasOwnProperty(elementId)) {
return;
}
var toolStateManager = cornerstoneTools.getElementToolStateManager(element);
if (toolStateManager === cornerstoneTools.globalImageIdSpecificToolStateManager) {
return;
}
toolStateManager.restoreToolState(appState.elementToolState[elementId]);
// Restore the saved viewport information
var savedViewport = appState.elementViewport[elementId];
cornerstone.setViewport(element, savedViewport);
// Update the element to apply the viewport and tool changes
cornerstone.updateImage(element);
});
return appState;
}
cornerstoneTools.appState = {
save: saveApplicationState,
restore: restoreApplicationState
};
})($, cornerstone, cornerstoneTools);
// End Source; src/stateManagement/applicationState.js
// Begin Source: src/stateManagement/frameOfReferenceStateManager.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
// This implements a frame-of-reference specific tool state management strategy. This means that
// measurement data are tied to a specific frame of reference UID and only visible to objects using
// that frame-of-reference UID
function newFrameOfReferenceSpecificToolStateManager() {
var toolState = {};
// here we add tool state, this is done by tools as well
// as modules that restore saved state
function addFrameOfReferenceSpecificToolState(frameOfReference, toolType, data) {
// if we don't have any tool state for this frameOfReference, add an empty object
if (toolState.hasOwnProperty(frameOfReference) === false) {
toolState[frameOfReference] = {};
}
var frameOfReferenceToolState = toolState[frameOfReference];
// if we don't have tool state for this type of tool, add an empty object
if (frameOfReferenceToolState.hasOwnProperty(toolType) === false) {
frameOfReferenceToolState[toolType] = {
data: []
};
}
var toolData = frameOfReferenceToolState[toolType];
// finally, add this new tool to the state
toolData.data.push(data);
}
// here you can get state - used by tools as well as modules
// that save state persistently
function getFrameOfReferenceSpecificToolState(frameOfReference, toolType) {
// if we don't have any tool state for this frame of reference, return undefined
if (toolState.hasOwnProperty(frameOfReference) === false) {
return;
}
var frameOfReferenceToolState = toolState[frameOfReference];
// if we don't have tool state for this type of tool, return undefined
if (frameOfReferenceToolState.hasOwnProperty(toolType) === false) {
return;
}
var toolData = frameOfReferenceToolState[toolType];
return toolData;
}
function removeFrameOfReferenceSpecificToolState(frameOfReference, toolType, data) {
// if we don't have any tool state for this frame of reference, return undefined
if (toolState.hasOwnProperty(frameOfReference) === false) {
return;
}
var frameOfReferenceToolState = toolState[frameOfReference];
// if we don't have tool state for this type of tool, return undefined
if (frameOfReferenceToolState.hasOwnProperty(toolType) === false) {
return;
}
var toolData = frameOfReferenceToolState[toolType];
// find this tool data
var indexOfData = -1;
for (var i = 0; i < toolData.data.length; i++) {
if (toolData.data[i] === data) {
indexOfData = i;
}
}
if (indexOfData !== -1) {
toolData.data.splice(indexOfData, 1);
}
}
var frameOfReferenceToolStateManager = {
get: getFrameOfReferenceSpecificToolState,
add: addFrameOfReferenceSpecificToolState,
remove: removeFrameOfReferenceSpecificToolState
};
return frameOfReferenceToolStateManager;
}
// 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;
})($, cornerstone, cornerstoneTools);
// End Source; src/stateManagement/frameOfReferenceStateManager.js
// Begin Source: src/stateManagement/imageIdSpecificStateManager.js
(function(cornerstone, cornerstoneTools) {
'use strict';
// This implements an imageId specific tool state management strategy. This means that
// measurements data is tied to a specific imageId and only visible for enabled elements
// that are displaying that imageId.
function newImageIdSpecificToolStateManager() {
var toolState = {};
// here we add tool state, this is done by tools as well
// as modules that restore saved state
function saveImageIdToolState(imageId) {
return toolState[imageId];
}
function restoreImageIdToolState(imageId, imageIdToolState) {
toolState[imageId] = imageIdToolState;
}
function saveToolState() {
return toolState;
}
function restoreToolState(savedToolState) {
toolState = savedToolState;
}
// here we add tool state, this is done by tools as well
// as modules that restore saved state
function addImageIdSpecificToolState(element, toolType, data) {
var enabledImage = cornerstone.getEnabledElement(element);
// if we don't have any tool state for this imageId, add an empty object
if (!enabledImage.image || toolState.hasOwnProperty(enabledImage.image.imageId) === false) {
toolState[enabledImage.image.imageId] = {};
}
var imageIdToolState = toolState[enabledImage.image.imageId];
// if we don't have tool state for this type of tool, add an empty object
if (imageIdToolState.hasOwnProperty(toolType) === false) {
imageIdToolState[toolType] = {
data: []
};
}
var toolData = imageIdToolState[toolType];
// finally, add this new tool to the state
toolData.data.push(data);
}
// here you can get state - used by tools as well as modules
// that save state persistently
function getImageIdSpecificToolState(element, toolType) {
var enabledImage = cornerstone.getEnabledElement(element);
// if we don't have any tool state for this imageId, return undefined
if (!enabledImage.image || toolState.hasOwnProperty(enabledImage.image.imageId) === false) {
return;
}
var imageIdToolState = toolState[enabledImage.image.imageId];
// if we don't have tool state for this type of tool, return undefined
if (imageIdToolState.hasOwnProperty(toolType) === false) {
return;
}
var toolData = imageIdToolState[toolType];
return toolData;
}
// Clears all tool data from this toolStateManager.
function clearImageIdSpecificToolStateManager(element) {
var enabledImage = cornerstone.getEnabledElement(element);
if (!enabledImage.image || toolState.hasOwnProperty(enabledImage.image.imageId) === false) {
return;
}
delete toolState[enabledImage.image.imageId];
}
var imageIdToolStateManager = {
get: getImageIdSpecificToolState,
add: addImageIdSpecificToolState,
clear: clearImageIdSpecificToolStateManager,
saveImageIdToolState: saveImageIdToolState,
restoreImageIdToolState: restoreImageIdToolState,
saveToolState: saveToolState,
restoreToolState: restoreToolState,
toolState: toolState
};
return imageIdToolStateManager;
}
// 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;
})(cornerstone, cornerstoneTools);
// End Source; src/stateManagement/imageIdSpecificStateManager.js
// Begin Source: src/stateManagement/loadHandlerManager.js
(function(cornerstoneTools) {
'use strict';
function loadHandlerManager() {
var defaultStartLoadHandler,
defaultEndLoadHandler,
defaultErrorLoadingHandler;
function setStartLoadHandler(handler) {
defaultStartLoadHandler = handler;
}
function getStartLoadHandler() {
return defaultStartLoadHandler;
}
function setEndLoadHandler(handler) {
defaultEndLoadHandler = handler;
}
function getEndLoadHandler(){
return defaultEndLoadHandler;
}
function setErrorLoadingHandler(handler) {
defaultErrorLoadingHandler = handler;
}
function getErrorLoadingHandler() {
return defaultErrorLoadingHandler;
}
var loadHandlers = {
setStartLoadHandler: setStartLoadHandler,
getStartLoadHandler: getStartLoadHandler,
setEndLoadHandler: setEndLoadHandler,
getEndLoadHandler: getEndLoadHandler,
setErrorLoadingHandler: setErrorLoadingHandler,
getErrorLoadingHandler: getErrorLoadingHandler
};
return loadHandlers;
}
// module/private exports
cornerstoneTools.loadHandlerManager = loadHandlerManager();
})(cornerstoneTools);
// End Source; src/stateManagement/loadHandlerManager.js
// Begin Source: src/stateManagement/stackSpecificStateManager.js
(function(cornerstone, cornerstoneTools) {
'use strict';
// This implements an Stack specific tool state management strategy. This means
// that tool data is shared between all imageIds in a given stack
function newStackSpecificToolStateManager(toolTypes, oldStateManager) {
var toolState = {};
function saveToolState() {
return toolState;
}
function restoreToolState(stackToolState) {
toolState = stackToolState;
}
// here we add tool state, this is done by tools as well
// as modules that restore saved state
function addStackSpecificToolState(element, toolType, data) {
// if this is a tool type to apply to the stack, do so
if (toolTypes.indexOf(toolType) >= 0) {
// if we don't have tool state for this type of tool, add an empty object
if (toolState.hasOwnProperty(toolType) === false) {
toolState[toolType] = {
data: []
};
}
var toolData = toolState[toolType];
// finally, add this new tool to the state
toolData.data.push(data);
} else {
// call the imageId specific tool state manager
return oldStateManager.add(element, toolType, data);
}
}
// here you can get state - used by tools as well as modules
// that save state persistently
function getStackSpecificToolState(element, toolType) {
// if this is a tool type to apply to the stack, do so
if (toolTypes.indexOf(toolType) >= 0) {
// if we don't have tool state for this type of tool, add an empty object
if (toolState.hasOwnProperty(toolType) === false) {
toolState[toolType] = {
data: []
};
}
var toolData = toolState[toolType];
return toolData;
} else {
// call the imageId specific tool state manager
return oldStateManager.get(element, toolType);
}
}
var stackSpecificToolStateManager = {
get: getStackSpecificToolState,
add: addStackSpecificToolState,
saveToolState: saveToolState,
restoreToolState: restoreToolState,
toolState: toolState,
};
return stackSpecificToolStateManager;
}
var stackStateManagers = [];
function addStackStateManager(element) {
var oldStateManager = cornerstoneTools.getElementToolStateManager(element);
if (!oldStateManager) {
oldStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager;
}
var stackTools = [ 'stack', 'stackPrefetch', 'playClip', 'volume', 'slab', 'referenceLines', 'crosshairs' ];
var stackSpecificStateManager = cornerstoneTools.newStackSpecificToolStateManager(stackTools, oldStateManager);
stackStateManagers.push(stackSpecificStateManager);
cornerstoneTools.setElementToolStateManager(element, stackSpecificStateManager);
}
// module/private exports
cornerstoneTools.newStackSpecificToolStateManager = newStackSpecificToolStateManager;
cornerstoneTools.addStackStateManager = addStackStateManager;
})(cornerstone, cornerstoneTools);
// End Source; src/stateManagement/stackSpecificStateManager.js
// Begin Source: src/stateManagement/textStyleManager.js
(function(cornerstoneTools) {
'use strict';
function textStyleManager() {
var defaultFontSize = 15,
defaultFont = defaultFontSize + 'px Arial',
defaultBackgroundColor = 'transparent';
function setFont(font) {
defaultFont = font;
}
function getFont() {
return defaultFont;
}
function setFontSize(fontSize) {
defaultFontSize = fontSize;
}
function getFontSize() {
return defaultFontSize;
}
function setBackgroundColor(backgroundColor) {
defaultBackgroundColor = backgroundColor;
}
function getBackgroundColor() {
return defaultBackgroundColor;
}
var textStyle = {
setFont: setFont,
getFont: getFont,
setFontSize: setFontSize,
getFontSize: getFontSize,
setBackgroundColor: setBackgroundColor,
getBackgroundColor: getBackgroundColor
};
return textStyle;
}
// module/private exports
cornerstoneTools.textStyle = textStyleManager();
})(cornerstoneTools);
// End Source; src/stateManagement/textStyleManager.js
// Begin Source: src/stateManagement/timeSeriesSpecificStateManager.js
(function(cornerstone, cornerstoneTools) {
'use strict';
// This implements an Stack specific tool state management strategy. This means
// that tool data is shared between all imageIds in a given stack
function newTimeSeriesSpecificToolStateManager(toolTypes, oldStateManager) {
var toolState = {};
// here we add tool state, this is done by tools as well
// as modules that restore saved state
function addStackSpecificToolState(element, toolType, data) {
// if this is a tool type to apply to the stack, do so
if (toolTypes.indexOf(toolType) >= 0) {
// if we don't have tool state for this type of tool, add an empty object
if (toolState.hasOwnProperty(toolType) === false) {
toolState[toolType] = {
data: []
};
}
var toolData = toolState[toolType];
// finally, add this new tool to the state
toolData.data.push(data);
} else {
// call the imageId specific tool state manager
return oldStateManager.add(element, toolType, data);
}
}
// here you can get state - used by tools as well as modules
// that save state persistently
function getStackSpecificToolState(element, toolType) {
// if this is a tool type to apply to the stack, do so
if (toolTypes.indexOf(toolType) >= 0) {
// if we don't have tool state for this type of tool, add an empty object
if (toolState.hasOwnProperty(toolType) === false) {
toolState[toolType] = {
data: []
};
}
var toolData = toolState[toolType];
return toolData;
} else {
// call the imageId specific tool state manager
return oldStateManager.get(element, toolType);
}
}
var imageIdToolStateManager = {
get: getStackSpecificToolState, add: addStackSpecificToolState
};
return imageIdToolStateManager;
}
var timeSeriesStateManagers = [];
function addTimeSeriesStateManager(element, tools) {
tools = tools || [ 'timeSeries' ];
var oldStateManager = cornerstoneTools.getElementToolStateManager(element);
if (oldStateManager === undefined) {
oldStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager;
}
var timeSeriesSpecificStateManager = cornerstoneTools.newTimeSeriesSpecificToolStateManager(tools, oldStateManager);
timeSeriesStateManagers.push(timeSeriesSpecificStateManager);
cornerstoneTools.setElementToolStateManager(element, timeSeriesSpecificStateManager);
}
// module/private exports
cornerstoneTools.newTimeSeriesSpecificToolStateManager = newTimeSeriesSpecificToolStateManager;
cornerstoneTools.addTimeSeriesStateManager = addTimeSeriesStateManager;
})(cornerstone, cornerstoneTools);
// End Source; src/stateManagement/timeSeriesSpecificStateManager.js
// Begin Source: src/stateManagement/toolColorManager.js
(function(cornerstoneTools) {
'use strict';
function toolColorManager(){
var defaultColor = 'white',
activeColor = 'greenyellow',
fillColor = 'transparent';
function setFillColor(color){
fillColor = color;
}
function getFillColor(){
return fillColor;
}
function setToolColor(color){
defaultColor = color;
}
function getToolColor(){
return defaultColor;
}
function setActiveToolColor(color){
activeColor = color;
}
function getActiveToolColor(){
return activeColor;
}
var toolColors = {
setFillColor: setFillColor,
getFillColor: getFillColor,
setToolColor: setToolColor,
getToolColor: getToolColor,
setActiveColor: setActiveToolColor,
getActiveColor: getActiveToolColor
};
return toolColors;
}
// module/private exports
cornerstoneTools.toolColors = toolColorManager();
})(cornerstoneTools);
// End Source; src/stateManagement/toolColorManager.js
// Begin Source: src/stateManagement/toolCoordinateManager.js
(function(cornerstoneTools) {
'use strict';
function toolCoordinateManager(){
var cooordsData = '';
function setActiveToolCoords(eventData){
cooordsData = eventData.currentPoints.canvas;
}
function getActiveToolCoords(){
return cooordsData;
}
var toolCoords = {
setCoords: setActiveToolCoords, getCoords: getActiveToolCoords
};
return toolCoords;
}
// module/private exports
cornerstoneTools.toolCoordinates = toolCoordinateManager();
})(cornerstoneTools);
// End Source; src/stateManagement/toolCoordinateManager.js
// Begin Source: src/stateManagement/toolStateManager.js
(function(cornerstone, cornerstoneTools) {
'use strict';
function getElementToolStateManager(element) {
var enabledImage = cornerstone.getEnabledElement(element);
// if the enabledImage has no toolStateManager, create a default one for it
// NOTE: This makes state management element specific
if (enabledImage.toolStateManager === undefined) {
enabledImage.toolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager;
}
return enabledImage.toolStateManager;
}
// here we add tool state, this is done by tools as well
// as modules that restore saved state
function addToolState(element, toolType, data) {
var toolStateManager = getElementToolStateManager(element);
toolStateManager.add(element, toolType, data);
// TODO: figure out how to broadcast this change to all enabled elements so they can update the image
// if this change effects them
}
// here you can get state - used by tools as well as modules
// that save state persistently
function getToolState(element, toolType) {
var toolStateManager = getElementToolStateManager(element);
return toolStateManager.get(element, toolType);
}
function removeToolState(element, toolType, data) {
var toolStateManager = getElementToolStateManager(element);
var toolData = toolStateManager.get(element, toolType);
// find this tool data
var indexOfData = -1;
for (var i = 0; i < toolData.data.length; i++) {
if (toolData.data[i] === data) {
indexOfData = i;
}
}
if (indexOfData !== -1) {
toolData.data.splice(indexOfData, 1);
}
}
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 = [];
}
}
// sets the tool state manager for an element
function setElementToolStateManager(element, toolStateManager) {
var enabledImage = cornerstone.getEnabledElement(element);
enabledImage.toolStateManager = toolStateManager;
}
// module/private exports
cornerstoneTools.addToolState = addToolState;
cornerstoneTools.getToolState = getToolState;
cornerstoneTools.removeToolState = removeToolState;
cornerstoneTools.clearToolState = clearToolState;
cornerstoneTools.setElementToolStateManager = setElementToolStateManager;
cornerstoneTools.getElementToolStateManager = getElementToolStateManager;
})(cornerstone, cornerstoneTools);
// End Source; src/stateManagement/toolStateManager.js
// Begin Source: src/stateManagement/toolStyleManager.js
(function(cornerstoneTools) {
'use strict';
function toolStyleManager() {
var defaultWidth = 1,
activeWidth = 2;
function setToolWidth(width){
defaultWidth = width;
}
function getToolWidth(){
return defaultWidth;
}
function setActiveToolWidth(width){
activeWidth = width;
}
function getActiveToolWidth(){
return activeWidth;
}
var toolStyle = {
setToolWidth: setToolWidth,
getToolWidth: getToolWidth,
setActiveWidth: setActiveToolWidth,
getActiveWidth: getActiveToolWidth
};
return toolStyle;
}
// module/private exports
cornerstoneTools.toolStyle = toolStyleManager();
})(cornerstoneTools);
// End Source; src/stateManagement/toolStyleManager.js
// Begin Source: src/stateManagement/touchSettingsManager.js
(function(cornerstoneTools) {
'use strict';
function touchSettingsManager() {
var defaultDistanceFromTouch = {
x: 0,
y: -45
};
function setToolDistanceFromTouch(distance){
defaultDistanceFromTouch = distance;
}
function getToolDistanceFromTouch(){
return defaultDistanceFromTouch;
}
var touchSettings = {
setToolDistanceFromTouch: setToolDistanceFromTouch,
getToolDistanceFromTouch: getToolDistanceFromTouch,
};
return touchSettings;
}
// module/private exports
cornerstoneTools.touchSettings = touchSettingsManager();
})(cornerstoneTools);
// End Source; src/stateManagement/touchSettingsManager.js
// Begin Source: src/synchronization/panZoomSynchronizer.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
// This function synchronizes the target zoom and pan to match the source
function panZoomSynchronizer(synchronizer, sourceElement, targetElement) {
// ignore the case where the source and target are the same enabled element
if (targetElement === sourceElement) {
return;
}
// get the source and target viewports
var sourceViewport = cornerstone.getViewport(sourceElement);
var targetViewport = cornerstone.getViewport(targetElement);
// do nothing if the scale and translation are the same
if (targetViewport.scale === sourceViewport.scale && targetViewport.translation.x === sourceViewport.translation.x && targetViewport.translation.y === sourceViewport.translation.y) {
return;
}
// scale and/or translation are different, sync them
targetViewport.scale = sourceViewport.scale;
targetViewport.translation.x = sourceViewport.translation.x;
targetViewport.translation.y = sourceViewport.translation.y;
synchronizer.setViewport(targetElement, targetViewport);
}
// module/private exports
cornerstoneTools.panZoomSynchronizer = panZoomSynchronizer;
})($, cornerstone, cornerstoneTools);
// End Source; src/synchronization/panZoomSynchronizer.js
// Begin Source: src/synchronization/stackImageIndexSynchronizer.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
// This function causes the image in the target stack to be set to the one closest
// to the image in the source stack by image position
function stackImageIndexSynchronizer(synchronizer, sourceElement, targetElement) {
// ignore the case where the source and target are the same enabled element
if (targetElement === sourceElement) {
return;
}
var sourceStackToolDataSource = cornerstoneTools.getToolState(sourceElement, 'stack');
var sourceStackData = sourceStackToolDataSource.data[0];
var targetStackToolDataSource = cornerstoneTools.getToolState(targetElement, 'stack');
var targetStackData = targetStackToolDataSource.data[0];
var newImageIdIndex = sourceStackData.currentImageIdIndex;
// clamp the index
newImageIdIndex = Math.min(Math.max(newImageIdIndex, 0), targetStackData.imageIds.length - 1);
// Do nothing if the index has not changed
if (newImageIdIndex === targetStackData.currentImageIdIndex) {
return;
}
var startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler();
var endLoadingHandler = cornerstoneTools.loadHandlerManager.getEndLoadHandler();
var errorLoadingHandler = cornerstoneTools.loadHandlerManager.getErrorLoadingHandler();
if (startLoadingHandler) {
startLoadingHandler(targetElement);
}
cornerstone.loadAndCacheImage(targetStackData.imageIds[newImageIdIndex]).then(function(image) {
var viewport = cornerstone.getViewport(targetElement);
targetStackData.currentImageIdIndex = newImageIdIndex;
synchronizer.displayImage(targetElement, image, viewport);
if (endLoadingHandler) {
endLoadingHandler(targetElement);
}
}, function(error) {
var imageId = targetStackData.imageIds[newImageIdIndex];
if (errorLoadingHandler) {
errorLoadingHandler(targetElement, imageId, error);
}
});
}
// module/private exports
cornerstoneTools.stackImageIndexSynchronizer = stackImageIndexSynchronizer;
})($, cornerstone, cornerstoneTools);
// End Source; src/synchronization/stackImageIndexSynchronizer.js
// Begin Source: src/synchronization/stackImagePositionOffsetSynchronizer.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
// This function causes the image in the target stack to be set to the one closest
// to the image in the source stack by image position
// In the future we will want to have a way to manually register links sets of the same orientation (e.g. an axial link set from a prior with an axial link set of a current). The user could do this by scrolling the two stacks to a similar location and then doing a user action (e.g. right click link) at which point the system will capture the delta between the image position (patient) of both stacks and use that to sync them. This offset will need to be adjustable.
function stackImagePositionOffsetSynchronizer(synchronizer, sourceElement, targetElement, eventData, positionDifference) {
// ignore the case where the source and target are the same enabled element
if (targetElement === sourceElement) {
return;
}
var sourceEnabledElement = cornerstone.getEnabledElement(sourceElement);
var sourceImagePlane = cornerstoneTools.metaData.get('imagePlane', sourceEnabledElement.image.imageId);
var sourceImagePosition = sourceImagePlane.imagePositionPatient;
var stackToolDataSource = cornerstoneTools.getToolState(targetElement, 'stack');
var stackData = stackToolDataSource.data[0];
var minDistance = Number.MAX_VALUE;
var newImageIdIndex = -1;
if (!positionDifference) {
return;
}
var finalPosition = sourceImagePosition.clone().add(positionDifference);
stackData.imageIds.forEach(function(imageId, index) {
var imagePlane = cornerstoneTools.metaData.get('imagePlane', imageId);
var imagePosition = imagePlane.imagePositionPatient;
var distance = finalPosition.distanceToSquared(imagePosition);
if (distance < minDistance) {
minDistance = distance;
newImageIdIndex = index;
}
});
if (newImageIdIndex === stackData.currentImageIdIndex || newImageIdIndex === -1) {
return;
}
var startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler();
var endLoadingHandler = cornerstoneTools.loadHandlerManager.getEndLoadHandler();
var errorLoadingHandler = cornerstoneTools.loadHandlerManager.getErrorLoadingHandler();
if (startLoadingHandler) {
startLoadingHandler(targetElement);
}
cornerstone.loadAndCacheImage(stackData.imageIds[newImageIdIndex]).then(function(image) {
var viewport = cornerstone.getViewport(targetElement);
stackData.currentImageIdIndex = newImageIdIndex;
synchronizer.displayImage(targetElement, image, viewport);
if (endLoadingHandler) {
endLoadingHandler(targetElement);
}
}, function(error) {
var imageId = stackData.imageIds[newImageIdIndex];
if (errorLoadingHandler) {
errorLoadingHandler(targetElement, imageId, error);
}
});
}
// module/private exports
cornerstoneTools.stackImagePositionOffsetSynchronizer = stackImagePositionOffsetSynchronizer;
})($, cornerstone, cornerstoneTools);
// End Source; src/synchronization/stackImagePositionOffsetSynchronizer.js
// Begin Source: src/synchronization/stackImagePositionSynchronizer.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
// This function causes the image in the target stack to be set to the one closest
// to the image in the source stack by image position
function stackImagePositionSynchronizer(synchronizer, sourceElement, targetElement) {
// ignore the case where the source and target are the same enabled element
if (targetElement === sourceElement) {
return;
}
var sourceImage = cornerstone.getEnabledElement(sourceElement).image;
var sourceImagePlane = cornerstoneTools.metaData.get('imagePlane', sourceImage.imageId);
var sourceImagePosition = sourceImagePlane.imagePositionPatient;
var stackToolDataSource = cornerstoneTools.getToolState(targetElement, 'stack');
var stackData = stackToolDataSource.data[0];
var minDistance = Number.MAX_VALUE;
var newImageIdIndex = -1;
$.each(stackData.imageIds, function(index, imageId) {
var imagePlane = cornerstoneTools.metaData.get('imagePlane', imageId);
var imagePosition = imagePlane.imagePositionPatient;
var distance = imagePosition.distanceToSquared(sourceImagePosition);
//console.log(index + '=' + distance);
if (distance < minDistance) {
minDistance = distance;
newImageIdIndex = index;
}
});
if (newImageIdIndex === stackData.currentImageIdIndex) {
return;
}
var startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler();
var endLoadingHandler = cornerstoneTools.loadHandlerManager.getEndLoadHandler();
var errorLoadingHandler = cornerstoneTools.loadHandlerManager.getErrorLoadingHandler();
if (startLoadingHandler) {
startLoadingHandler(targetElement);
}
if (newImageIdIndex !== -1) {
cornerstone.loadAndCacheImage(stackData.imageIds[newImageIdIndex]).then(function(image) {
var viewport = cornerstone.getViewport(targetElement);
stackData.currentImageIdIndex = newImageIdIndex;
synchronizer.displayImage(targetElement, image, viewport);
if (endLoadingHandler) {
endLoadingHandler(targetElement);
}
}, function(error) {
var imageId = stackData.imageIds[newImageIdIndex];
if (errorLoadingHandler) {
errorLoadingHandler(targetElement, imageId, error);
}
});
}
}
// module/private exports
cornerstoneTools.stackImagePositionSynchronizer = stackImagePositionSynchronizer;
})($, cornerstone, cornerstoneTools);
// End Source; src/synchronization/stackImagePositionSynchronizer.js
// Begin Source: src/synchronization/stackScrollSynchronizer.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
// 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
if (sourceElement === targetElement) {
return;
}
// If there is no event, or direction is 0, stop
if (!eventData || !eventData.direction) {
return;
}
// Get the stack of the target viewport
var stackToolDataSource = cornerstoneTools.getToolState(targetElement, 'stack');
var stackData = stackToolDataSource.data[0];
// Get the new index for the stack
var newImageIdIndex = stackData.currentImageIdIndex + eventData.direction;
// Ensure the index does not exceed the bounds of the stack
newImageIdIndex = Math.min(Math.max(newImageIdIndex, 0), stackData.imageIds.length - 1);
// If the index has not changed, stop here
if (stackData.currentImageIdIndex === newImageIdIndex) {
return;
}
var startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler();
var endLoadingHandler = cornerstoneTools.loadHandlerManager.getEndLoadHandler();
var errorLoadingHandler = cornerstoneTools.loadHandlerManager.getErrorLoadingHandler();
if (startLoadingHandler) {
startLoadingHandler(targetElement);
}
cornerstone.loadAndCacheImage(stackData.imageIds[newImageIdIndex]).then(function(image) {
var viewport = cornerstone.getViewport(targetElement);
stackData.currentImageIdIndex = newImageIdIndex;
synchronizer.displayImage(targetElement, image, viewport);
if (endLoadingHandler) {
endLoadingHandler(targetElement);
}
}, function(error) {
var imageId = stackData.imageIds[newImageIdIndex];
if (errorLoadingHandler) {
errorLoadingHandler(targetElement, imageId, error);
}
});
}
// module/private exports
cornerstoneTools.stackScrollSynchronizer = stackScrollSynchronizer;
})($, cornerstone, cornerstoneTools);
// End Source; src/synchronization/stackScrollSynchronizer.js
// Begin Source: src/synchronization/synchronizer.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
// This object is responsible for synchronizing target elements when an event fires on a source
// element
function Synchronizer(event, handler) {
var that = this;
var sourceElements = []; // source elements fire the events we want to synchronize to
var targetElements = []; // target elements we want to synchronize to source elements
var ignoreFiredEvents = false;
var initialData = {};
var eventHandler = handler;
this.setHandler = function(handler) {
eventHandler = handler;
};
this.getHandler = function() {
return eventHandler;
};
this.getDistances = function() {
if (!sourceElements.length || !targetElements.length) {
return;
}
initialData.distances = {};
initialData.imageIds = {
sourceElements: [],
targetElements: []
};
sourceElements.forEach(function(sourceElement) {
var sourceEnabledElement = cornerstone.getEnabledElement(sourceElement);
var sourceImageId = sourceEnabledElement.image.imageId;
var sourceImagePlane = cornerstoneTools.metaData.get('imagePlane', sourceImageId);
var sourceImagePosition = sourceImagePlane.imagePositionPatient;
if (initialData.hasOwnProperty(sourceEnabledElement)) {
return;
} else {
initialData.distances[sourceImageId] = {};
}
initialData.imageIds.sourceElements.push(sourceImageId);
targetElements.forEach(function(targetElement) {
var targetEnabledElement = cornerstone.getEnabledElement(targetElement);
var targetImageId = targetEnabledElement.image.imageId;
initialData.imageIds.targetElements.push(targetImageId);
if (sourceElement === targetElement) {
return;
}
if (sourceImageId === targetImageId) {
return;
}
if (initialData.distances[sourceImageId].hasOwnProperty(targetImageId)) {
return;
}
var targetImagePlane = cornerstoneTools.metaData.get('imagePlane', targetImageId);
var targetImagePosition = targetImagePlane.imagePositionPatient;
initialData.distances[sourceImageId][targetImageId] = targetImagePosition.clone().sub(sourceImagePosition);
});
if (!Object.keys(initialData.distances[sourceImageId]).length) {
delete initialData.distances[sourceImageId];
}
});
};
function fireEvent(sourceElement, eventData) {
// Broadcast an event that something changed
if (!sourceElements.length || !targetElements.length) {
return;
}
ignoreFiredEvents = true;
targetElements.forEach(function(targetElement) {
var targetIndex = targetElements.indexOf(targetElement);
if (targetIndex === -1) {
return;
}
var targetImageId = initialData.imageIds.targetElements[targetIndex];
var sourceIndex = sourceElements.indexOf(sourceElement);
if (sourceIndex === -1) {
return;
}
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;
}
function onEvent(e, eventData) {
if (ignoreFiredEvents === true) {
return;
}
fireEvent(e.currentTarget, eventData);
}
// adds an element as a source
this.addSource = function(element) {
// Return if this element was previously added
var index = sourceElements.indexOf(element);
if (index !== -1) {
return;
}
// Add to our list of enabled elements
sourceElements.push(element);
// subscribe to the event
$(element).on(event, onEvent);
// Update the inital distances between elements
that.getDistances();
that.updateDisableHandlers();
};
// adds an element as a target
this.addTarget = function(element) {
// Return if this element was previously added
var index = targetElements.indexOf(element);
if (index !== -1) {
return;
}
// Add to our list of enabled elements
targetElements.push(element);
// Update the inital distances between elements
that.getDistances();
// Invoke the handler for this new target element
eventHandler(that, element, element, 0);
that.updateDisableHandlers();
};
// adds an element as both a source and a target
this.add = function(element) {
that.addSource(element);
that.addTarget(element);
};
// removes an element as a source
this.removeSource = function(element) {
// Find the index of this element
var index = sourceElements.indexOf(element);
if (index === -1) {
return;
}
// remove this element from the array
sourceElements.splice(index, 1);
// stop listening for the event
$(element).off(event, onEvent);
// Update the inital distances between elements
that.getDistances();
// Update everyone listening for events
fireEvent(element);
that.updateDisableHandlers();
};
// removes an element as a target
this.removeTarget = function(element) {
// Find the index of this element
var index = targetElements.indexOf(element);
if (index === -1) {
return;
}
// remove this element from the array
targetElements.splice(index, 1);
// Update the inital distances between elements
that.getDistances();
// Invoke the handler for the removed target
eventHandler(that, element, element, 0);
that.updateDisableHandlers();
};
// removes an element as both a source and target
this.remove = function(element) {
that.removeTarget(element);
that.removeSource(element);
};
// returns the source elements
this.getSourceElements = function() {
return sourceElements;
};
// returns the target elements
this.getTargetElements = function() {
return targetElements;
};
this.displayImage = function(element, image, viewport) {
ignoreFiredEvents = true;
cornerstone.displayImage(element, image, viewport);
ignoreFiredEvents = false;
};
this.setViewport = function(element, viewport) {
ignoreFiredEvents = true;
cornerstone.setViewport(element, viewport);
ignoreFiredEvents = false;
};
function disableHandler(e, eventData) {
var element = eventData.element;
that.remove(element);
}
this.updateDisableHandlers = function() {
var elements = $.unique(sourceElements.concat(targetElements));
elements.forEach(function(element) {
$(element).off('CornerstoneElementDisabled', disableHandler);
$(element).on('CornerstoneElementDisabled', disableHandler);
});
};
this.destroy = function() {
var elements = $.unique(sourceElements.concat(targetElements));
elements.forEach(function(element) {
that.remove(element);
});
};
}
// module/private exports
cornerstoneTools.Synchronizer = Synchronizer;
})($, cornerstone, cornerstoneTools);
// End Source; src/synchronization/synchronizer.js
// Begin Source: src/synchronization/updateImageSynchronizer.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
// This function causes the target image to be drawn immediately
function updateImageSynchronizer(synchronizer, sourceElement, targetElement) {
// ignore the case where the source and target are the same enabled element
if (targetElement === sourceElement) {
return;
}
cornerstone.updateImage(targetElement);
}
// module/private exports
cornerstoneTools.updateImageSynchronizer = updateImageSynchronizer;
})($, cornerstone, cornerstoneTools);
// End Source; src/synchronization/updateImageSynchronizer.js
// Begin Source: src/synchronization/wwwcSynchronizer.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
// This function synchronizes the target element ww/wc to match the source element
function wwwcSynchronizer(synchronizer, sourceElement, targetElement) {
// ignore the case where the source and target are the same enabled element
if (targetElement === sourceElement) {
return;
}
// get the source and target viewports
var sourceViewport = cornerstone.getViewport(sourceElement);
var targetViewport = cornerstone.getViewport(targetElement);
// do nothing if the ww/wc already match
if (targetViewport.voi.windowWidth === sourceViewport.voi.windowWidth && targetViewport.voi.windowCenter === sourceViewport.voi.windowCenter && targetViewport.invert === sourceViewport.invert) {
return;
}
// www/wc are different, sync them
targetViewport.voi.windowWidth = sourceViewport.voi.windowWidth;
targetViewport.voi.windowCenter = sourceViewport.voi.windowCenter;
targetViewport.invert = sourceViewport.invert;
synchronizer.setViewport(targetElement, targetViewport);
}
// module/private exports
cornerstoneTools.wwwcSynchronizer = wwwcSynchronizer;
})($, cornerstone, cornerstoneTools);
// End Source; src/synchronization/wwwcSynchronizer.js
// Begin Source: src/timeSeriesTools/ProbeTool4D.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
var toolType = 'probe4D';
function updateLineSample(measurementData) {
var samples = [];
measurementData.timeSeries.stacks.forEach(function(stack) {
cornerstone.loadAndCacheImage(stack.imageIds[measurementData.imageIdIndex]).then(function(image) {
var offset = Math.round(measurementData.handles.end.x) + Math.round(measurementData.handles.end.y) * image.width;
var sample = image.getPixelData()[offset];
samples.push(sample);
//console.log(sample);
});
});
measurementData.lineSample.set(samples);
}
///////// BEGIN ACTIVE TOOL ///////
function createNewMeasurement(mouseEventData) {
var timeSeriestoolData = cornerstoneTools.getToolState(mouseEventData.element, 'timeSeries');
if (timeSeriestoolData === undefined || timeSeriestoolData.data === undefined || timeSeriestoolData.data.length === 0) {
return;
}
var timeSeries = timeSeriestoolData.data[0];
// create the measurement data for this tool with the end handle activated
var measurementData = {
timeSeries: timeSeries, lineSample: new cornerstoneTools.LineSampleMeasurement(), imageIdIndex: timeSeries.stacks[timeSeries.currentStackIndex].currentImageIdIndex, visible: true, handles: {
end: {
x: mouseEventData.currentPoints.image.x,
y: mouseEventData.currentPoints.image.y,
highlight: true,
active: true
}
}
};
updateLineSample(measurementData);
cornerstoneTools.MeasurementManager.add(measurementData);
return measurementData;
}
///////// END ACTIVE TOOL ///////
///////// 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 === undefined) {
return;
}
// we have tool data for this element - iterate over each one and draw it
var context = eventData.canvasContext.canvas.getContext('2d');
cornerstone.setToPixelCoordinateSystem(eventData.enabledElement, context);
var color = 'white';
for (var i = 0; i < toolData.data.length; i++) {
context.save();
var data = toolData.data[i];
// draw the handles
context.beginPath();
cornerstoneTools.drawHandles(context, eventData, data.handles, color);
context.stroke();
// Draw text
var fontParameters = cornerstoneTools.setContextToDisplayFontSize(eventData.enabledElement, eventData.canvasContext, 15);
context.font = '' + fontParameters.fontSize + 'px Arial';
// translate the x/y away from the cursor
var x = Math.round(data.handles.end.x);
var y = Math.round(data.handles.end.y);
var textX = data.handles.end.x + 3;
var textY = data.handles.end.y - 3;
context.fillStyle = color;
context.fillText('' + x + ',' + y, textX, textY);
context.restore();
}
}
///////// END IMAGE RENDERING ///////
// module exports
cornerstoneTools.probeTool4D = cornerstoneTools.mouseButtonTool({
createNewMeasurement: createNewMeasurement, onImageRendered: onImageRendered, toolType: toolType
});
})($, cornerstone, cornerstoneTools);
// End Source; src/timeSeriesTools/ProbeTool4D.js
// Begin Source: src/timeSeriesTools/timeSeries.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function incrementTimePoint(element, timePoints, wrap) {
var toolData = cornerstoneTools.getToolState(element, 'timeSeries');
if (toolData === undefined || toolData.data === undefined || toolData.data.length === 0) {
return;
}
var timeSeriesData = toolData.data[0];
var currentStack = timeSeriesData.stacks[timeSeriesData.currentStackIndex];
var currentImageIdIndex = currentStack.currentImageIdIndex;
var newStackIndex = timeSeriesData.currentStackIndex + timePoints;
// loop around if we go outside the stack
if (wrap) {
if (newStackIndex >= timeSeriesData.stacks.length) {
newStackIndex = 0;
}
if (newStackIndex < 0) {
newStackIndex = timeSeriesData.stacks.length - 1;
}
} else {
newStackIndex = Math.min(timeSeriesData.stacks.length - 1, newStackIndex);
newStackIndex = Math.max(0, newStackIndex);
}
if (newStackIndex !== timeSeriesData.currentStackIndex) {
var viewport = cornerstone.getViewport(element);
var newStack = timeSeriesData.stacks[newStackIndex];
var startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler();
var endLoadingHandler = cornerstoneTools.loadHandlerManager.getEndLoadHandler();
var errorLoadingHandler = cornerstoneTools.loadHandlerManager.getErrorLoadingHandler();
if (startLoadingHandler) {
startLoadingHandler(element);
}
cornerstone.loadAndCacheImage(newStack.imageIds[currentImageIdIndex]).then(function(image) {
if (timeSeriesData.currentImageIdIndex !== currentImageIdIndex) {
newStack.currentImageIdIndex = currentImageIdIndex;
timeSeriesData.currentStackIndex = newStackIndex;
cornerstone.displayImage(element, image, viewport);
if (endLoadingHandler) {
endLoadingHandler(element);
}
}
}, function(error) {
var imageId = newStack.imageIds[currentImageIdIndex];
if (errorLoadingHandler) {
errorLoadingHandler(element, imageId, error);
}
});
}
}
// module/private exports
cornerstoneTools.incrementTimePoint = incrementTimePoint;
})($, cornerstone, cornerstoneTools);
// End Source; src/timeSeriesTools/timeSeries.js
// Begin Source: src/timeSeriesTools/timeSeriesPlayer.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
var toolType = 'timeSeriesPlayer';
/**
* Starts playing a clip or adjusts the frame rate of an already playing clip. framesPerSecond is
* optional and defaults to 30 if not specified. A negative framesPerSecond will play the clip in reverse.
* The element must be a stack of images
* @param element
* @param framesPerSecond
*/
function playClip(element, framesPerSecond) {
if (element === undefined) {
throw 'playClip: element must not be undefined';
}
if (framesPerSecond === undefined) {
framesPerSecond = 30;
}
var timeSeriesToolData = cornerstoneTools.getToolState(element, 'timeSeries');
if (timeSeriesToolData === undefined || timeSeriesToolData.data === undefined || timeSeriesToolData.data.length === 0) {
return;
}
var playClipToolData = cornerstoneTools.getToolState(element, toolType);
var playClipData;
if (playClipToolData === undefined || playClipToolData.data.length === 0) {
playClipData = {
intervalId: undefined, framesPerSecond: framesPerSecond, lastFrameTimeStamp: undefined, frameRate: 0
};
cornerstoneTools.addToolState(element, toolType, playClipData);
} else {
playClipData = playClipToolData.data[0];
playClipData.framesPerSecond = framesPerSecond;
}
// if already playing, do not set a new interval
if (playClipData.intervalId !== undefined) {
return;
}
playClipData.intervalId = setInterval(function() {
if (playClipData.framesPerSecond > 0) {
cornerstoneTools.incrementTimePoint(element, 1, true);
} else {
cornerstoneTools.incrementTimePoint(element, -1, true);
}
}, 1000 / Math.abs(playClipData.framesPerSecond));
}
/**
* Stops an already playing clip.
* * @param element
*/
function stopClip(element) {
var playClipToolData = cornerstoneTools.getToolState(element, toolType);
var playClipData;
if (playClipToolData === undefined || playClipToolData.data.length === 0) {
return;
} else {
playClipData = playClipToolData.data[0];
}
clearInterval(playClipData.intervalId);
playClipData.intervalId = undefined;
}
// module/private exports
cornerstoneTools.timeSeriesPlayer = {
start: playClip, stop: stopClip
};
})($, cornerstone, cornerstoneTools);
// End Source; src/timeSeriesTools/timeSeriesPlayer.js
// Begin Source: src/timeSeriesTools/timeSeriesScroll.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function mouseUpCallback(e, eventData) {
$(eventData.element).off('CornerstoneToolsMouseDrag', mouseDragCallback);
$(eventData.element).off('CornerstoneToolsMouseUp', mouseUpCallback);
}
function mouseDownCallback(e, eventData) {
if (cornerstoneTools.isMouseButtonEnabled(eventData.which, e.data.mouseButtonMask)) {
var mouseDragEventData = {
deltaY: 0,
options: e.data.options
};
$(eventData.element).on('CornerstoneToolsMouseDrag', mouseDragEventData, mouseDragCallback);
$(eventData.element).on('CornerstoneToolsMouseUp', mouseUpCallback);
e.stopImmediatePropagation();
return false;
}
}
function mouseDragCallback(e, eventData) {
e.data.deltaY += eventData.deltaPoints.page.y;
var toolData = cornerstoneTools.getToolState(eventData.element, 'timeSeries');
if (toolData === undefined || toolData.data === undefined || toolData.data.length === 0) {
return;
}
var timeSeriesData = toolData.data[0];
var pixelsPerTimeSeries = $(eventData.element).height() / timeSeriesData.stacks.length ;
if (e.data.options !== undefined && e.data.options.timeSeriesScrollSpeed !== undefined) {
pixelsPerTimeSeries = e.data.options.timeSeriesScrollSpeed;
}
if (e.data.deltaY >= pixelsPerTimeSeries || e.data.deltaY <= -pixelsPerTimeSeries) {
var timeSeriesDelta = Math.round(e.data.deltaY / pixelsPerTimeSeries);
var timeSeriesDeltaMod = e.data.deltaY % pixelsPerTimeSeries;
cornerstoneTools.incrementTimePoint(eventData.element, timeSeriesDelta);
e.data.deltaY = timeSeriesDeltaMod;
}
return false; // false = cases jquery to preventDefault() and stopPropagation() this event
}
function mouseWheelCallback(e, eventData) {
var images = -eventData.direction;
cornerstoneTools.incrementTimePoint(eventData.element, images);
}
function onDrag(e) {
var mouseMoveData = e.originalEvent.detail;
var eventData = {
deltaY: 0
};
eventData.deltaY += mouseMoveData.deltaPoints.page.y;
var toolData = cornerstoneTools.getToolState(mouseMoveData.element, 'stack');
if (toolData === undefined || toolData.data === undefined || toolData.data.length === 0) {
return;
}
if (eventData.deltaY >= 3 || eventData.deltaY <= -3) {
var timeSeriesDelta = eventData.deltaY / 3;
var timeSeriesDeltaMod = eventData.deltaY % 3;
cornerstoneTools.setTimePoint(eventData.element, timeSeriesDelta);
eventData.deltaY = timeSeriesDeltaMod;
}
return false; // false = cases jquery to preventDefault() and stopPropagation() this event
}
// module/private exports
cornerstoneTools.timeSeriesScroll = cornerstoneTools.simpleMouseButtonTool(mouseDownCallback);
cornerstoneTools.timeSeriesScrollWheel = cornerstoneTools.mouseWheelTool(mouseWheelCallback);
cornerstoneTools.timeSeriesScrollTouchDrag = cornerstoneTools.touchDragTool(onDrag);
})($, cornerstone, cornerstoneTools);
// End Source; src/timeSeriesTools/timeSeriesScroll.js
// Begin Source: src/util/RoundToDecimal.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
function roundToDecimal(value, precision) {
var multiplier = Math.pow(10, precision);
return (Math.round(value * multiplier) / multiplier);
}
// module exports
cornerstoneTools.roundToDecimal = roundToDecimal;
})($, cornerstone, cornerstoneTools);
// End Source; src/util/RoundToDecimal.js
// Begin Source: src/util/calculateSUV.js
(function(cornerstoneTools) {
'use strict';
function calculateSUV(image, storedPixelValue) {
// if no dicom data set, return undefined
if (image.data === undefined) {
return undefined;
}
// image must be PET
if (image.data.string('x00080060') !== 'PT') {
return undefined;
}
var modalityPixelValue = storedPixelValue * image.slope + image.intercept;
var patientWeight = image.data.floatString('x00101030'); // in kg
if (patientWeight === undefined) {
return undefined;
}
var petSequence = image.data.elements.x00540016;
if (petSequence === undefined) {
return undefined;
}
petSequence = petSequence.items[0].dataSet;
var startTime = petSequence.time('x00181072');
var totalDose = petSequence.floatString('x00181074');
var halfLife = petSequence.floatString('x00181075');
var acquisitionTime = image.data.time('x00080032');
if (!startTime || !totalDose || !halfLife || !acquisitionTime) {
return undefined;
}
var acquisitionTimeInSeconds = acquisitionTime.fractionalSeconds + acquisitionTime.seconds + acquisitionTime.minutes * 60 + acquisitionTime.hours * 60 * 60;
var injectionStartTimeInSeconds = startTime.fractionalSeconds + startTime.seconds + startTime.minutes * 60 + startTime.hours * 60 * 60;
var durationInSeconds = acquisitionTimeInSeconds - injectionStartTimeInSeconds;
var correctedDose = totalDose * Math.exp(-durationInSeconds * Math.log(2) / halfLife);
var suv = modalityPixelValue * patientWeight / correctedDose * 1000;
return suv;
}
// module exports
cornerstoneTools.calculateSUV = calculateSUV;
})(cornerstoneTools);
// End Source; src/util/calculateSUV.js
// Begin Source: src/util/copyPoints.js
(function($, cornerstone, cornerstoneMath, cornerstoneTools) {
'use strict';
function copyPoints(points) {
var page = cornerstoneMath.point.copy(points.page);
var image = cornerstoneMath.point.copy(points.image);
var client = cornerstoneMath.point.copy(points.client);
var canvas = cornerstoneMath.point.copy(points.canvas);
return {
page: page, image: image, client: client, canvas: canvas
};
}
// module exports
cornerstoneTools.copyPoints = copyPoints;
})($, cornerstone, cornerstoneMath, cornerstoneTools);
// End Source; src/util/copyPoints.js
// Begin Source: src/util/drawEllipse.js
(function(cornerstoneTools) {
'use strict';
// http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
function drawEllipse(context, x, y, w, h) {
var kappa = 0.5522848,
ox = (w / 2) * kappa, // control point offset horizontal
oy = (h / 2) * kappa, // control point offset vertical
xe = x + w, // x-end
ye = y + h, // y-end
xm = x + w / 2, // x-middle
ym = y + h / 2; // y-middle
context.beginPath();
context.moveTo(x, ym);
context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
context.closePath();
context.stroke();
}
// Module exports
cornerstoneTools.drawEllipse = drawEllipse;
})(cornerstoneTools);
// End Source; src/util/drawEllipse.js
// Begin Source: src/util/drawTextBox.js
(function(cornerstoneTools) {
'use strict';
function drawTextBox(context, text, x, y, color) {
var padding = 5,
font = cornerstoneTools.textStyle.getFont(),
fontSize = cornerstoneTools.textStyle.getFontSize(),
backgroundColor = cornerstoneTools.textStyle.getBackgroundColor();
context.save();
// Get the text width in the current font
context.font = font;
var width = context.measureText(text).width;
// Draw the background box with padding
context.textBaseline = 'top';
context.fillStyle = backgroundColor;
context.fillRect(x, y - fontSize, width + (padding * 2), fontSize + (padding * 2));
// Draw the text
context.fillStyle = color;
context.fillText(text, x + padding, y - fontSize + padding);
context.restore();
}
// module exports
cornerstoneTools.drawTextBox = drawTextBox;
})(cornerstoneTools);
// End Source; src/util/drawTextBox.js
// Begin Source: src/util/getLuminance.js
(function(cornerstone, cornerstoneTools) {
'use strict';
function getLuminance(element, x, y, width, height) {
if (!element) {
throw 'getLuminance: parameter element must not be undefined';
}
x = Math.round(x);
y = Math.round(y);
var enabledElement = cornerstone.getEnabledElement(element);
var luminance = [];
var index = 0;
var pixelData = enabledElement.image.getPixelData();
var spIndex,
row,
column;
if (enabledElement.image.color) {
for (row = 0; row < height; row++) {
for (column = 0; column < width; column++) {
spIndex = (((row + y) * enabledElement.image.columns) + (column + x)) * 4;
var red = pixelData[spIndex];
var green = pixelData[spIndex + 1];
var blue = pixelData[spIndex + 2];
luminance[index++] = 0.2126 * red + 0.7152 * green + 0.0722 * blue;
}
}
} else {
for (row = 0; row < height; row++) {
for (column = 0; column < width; column++) {
spIndex = ((row + y) * enabledElement.image.columns) + (column + x);
luminance[index++] = pixelData[spIndex];
}
}
}
return luminance;
}
// module exports
cornerstoneTools.getLuminance = getLuminance;
})(cornerstone, cornerstoneTools);
// End Source; src/util/getLuminance.js
// Begin Source: src/util/getMaxSimultaneousRequests.js
(function(cornerstone, cornerstoneTools) {
'use strict';
// Maximum concurrent connections to the same server
// Information from http://sgdev-blog.blogspot.fr/2014/01/maximum-concurrent-connection-to-same.html
var maxSimultaneousRequests = {
default: 6,
IE: {
9: 6,
10: 8,
default: 8
},
Firefox: {
default: 6
},
Opera: {
10: 8,
11: 6,
12: 6,
default: 6
},
Chrome: {
default: 6
},
Safari: {
default: 4
}
};
// Browser name / version detection
// http://stackoverflow.com/questions/2400935/browser-detection-in-javascript
function getBrowserInfo() {
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] || '');
}
if (M[1] === 'Chrome') {
tem = ua.match(/\b(OPR|Edge)\/(\d+)/);
if (tem !== null) {
return tem.slice(1).join(' ').replace('OPR', 'Opera');
}
}
M = M[2]? [ M[1], M[2] ]: [ navigator.appName, navigator.appVersion, '-?' ];
if ((tem = ua.match(/version\/(\d+)/i)) !== null) {
M.splice(1, 1, tem[1]);
}
return M.join(' ');
}
function getMaxSimultaneousRequests() {
var config = cornerstoneTools.stackPrefetch.getConfiguration();
// Give preference to user-chosen values
if (config.maxSimultaneousRequests) {
return config.maxSimultaneousRequests;
}
var infoString = getBrowserInfo();
var info = infoString.split(' ');
var browserName = info[0];
var browserVersion = info[1];
var browserData = maxSimultaneousRequests[browserName];
if (!browserData) {
return maxSimultaneousRequests['default'];
}
if (!browserData[browserVersion]) {
return browserData['default'];
}
return browserData[browserVersion];
}
// module exports
cornerstoneTools.getMaxSimultaneousRequests = getMaxSimultaneousRequests;
cornerstoneTools.getBrowserInfo = getBrowserInfo;
})(cornerstone, cornerstoneTools);
// End Source; src/util/getMaxSimultaneousRequests.js
// Begin Source: src/util/getRGBPixels.js
(function(cornerstone, cornerstoneTools) {
'use strict';
function getRGBPixels(element, x, y, width, height) {
if (!element) {
throw 'getRGBPixels: parameter element must not be undefined';
}
x = Math.round(x);
y = Math.round(y);
var enabledElement = cornerstone.getEnabledElement(element);
var storedPixelData = [];
var index = 0;
var pixelData = enabledElement.image.getPixelData();
var spIndex,
row,
column;
if (enabledElement.image.color) {
for (row = 0; row < height; row++) {
for (column = 0; column < width; column++) {
spIndex = (((row + y) * enabledElement.image.columns) + (column + x)) * 4;
var red = pixelData[spIndex];
var green = pixelData[spIndex + 1];
var blue = pixelData[spIndex + 2];
var alpha = pixelData[spIndex + 3];
storedPixelData[index++] = red;
storedPixelData[index++] = green;
storedPixelData[index++] = blue;
storedPixelData[index++] = alpha;
}
}
}
return storedPixelData;
}
// module exports
cornerstoneTools.getRGBPixels = getRGBPixels;
})(cornerstone, cornerstoneTools);
// End Source; src/util/getRGBPixels.js
// Begin Source: src/util/isMouseButtonEnabled.js
(function(cornerstone, cornerstoneTools) {
'use strict';
function isMouseButtonEnabled(which, mouseButtonMask) {
/*jshint bitwise: false*/
var mouseButton = (1 << (which - 1));
return ((mouseButtonMask & mouseButton) !== 0);
}
// module exports
cornerstoneTools.isMouseButtonEnabled = isMouseButtonEnabled;
})(cornerstone, cornerstoneTools);
// End Source; src/util/isMouseButtonEnabled.js
// Begin Source: src/util/pauseEvent.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
/**
* This function is used to prevent selection from occuring when left click dragging on the image
* @param e the event that is provided to your event handler
* Based on: http://stackoverflow.com/questions/5429827/how-can-i-prevent-text-element-selection-with-cursor-drag
* @returns {boolean}
*/
function pauseEvent(e) {
if (e.stopPropagation) {
e.stopPropagation();
}
if (e.preventDefault) {
e.preventDefault();
}
e.cancelBubble = true;
e.returnValue = false;
return false;
}
// module exports
cornerstoneTools.pauseEvent = pauseEvent;
})($, cornerstone, cornerstoneTools);
// End Source; src/util/pauseEvent.js
// Begin Source: src/util/pointProjector.js
(function($, cornerstone, cornerstoneTools) {
'use strict';
// projects a patient point to an image point
function projectPatientPointToImagePlane(patientPoint, imagePlane) {
var point = patientPoint.clone().sub(imagePlane.imagePositionPatient);
var x = imagePlane.rowCosines.dot(point) / imagePlane.columnPixelSpacing;
var y = imagePlane.columnCosines.dot(point) / imagePlane.rowPixelSpacing;
return {
x: x, y: y
};
}
// projects an image point to a patient point
function imagePointToPatientPoint(imagePoint, imagePlane) {
var x = imagePlane.rowCosines.clone().multiplyScalar(imagePoint.x);
x.multiplyScalar(imagePlane.columnPixelSpacing);
var y = imagePlane.columnCosines.clone().multiplyScalar(imagePoint.y);
y.multiplyScalar(imagePlane.rowPixelSpacing);
var patientPoint = x.add(y);
patientPoint.add(imagePlane.imagePositionPatient);
return patientPoint;
}
function getRectangleFromImagePlane(imagePlane) {
// Get the points
var topLeft = imagePointToPatientPoint({
x: 0,
y: 0
}, imagePlane);
var topRight = imagePointToPatientPoint({
x: imagePlane.columns,
y: 0
}, imagePlane);
var bottomLeft = imagePointToPatientPoint({
x: 0,
y: imagePlane.rows
}, imagePlane);
var bottomRight = imagePointToPatientPoint({
x: imagePlane.columns,
y: imagePlane.rows
}, imagePlane);
// Get each side as a vector
var rect = {
top: new cornerstoneMath.Line3(topLeft, topRight),
left: new cornerstoneMath.Line3(topLeft, bottomLeft),
right: new cornerstoneMath.Line3(topRight, bottomRight),
bottom: new cornerstoneMath.Line3(bottomLeft, bottomRight),
};
return rect;
}
function lineRectangleIntersection(line, rect) {
var intersections = [];
Object.keys(rect).forEach(function(side) {
var segment = rect[side];
var intersection = line.intersectLine(segment);
if (intersection) {
intersections.push(intersection);
}
});
return intersections;
}
function planePlaneIntersection(targetImagePlane, referenceImagePlane) {
// Gets the line of intersection between two planes in patient space
// First, get the normals of each image plane
var targetNormal = targetImagePlane.rowCosines.clone().cross(targetImagePlane.columnCosines);
var targetPlane = new cornerstoneMath.Plane();
targetPlane.setFromNormalAndCoplanarPoint(targetNormal, targetImagePlane.imagePositionPatient);
var referenceNormal = referenceImagePlane.rowCosines.clone().cross(referenceImagePlane.columnCosines);
var referencePlane = new cornerstoneMath.Plane();
referencePlane.setFromNormalAndCoplanarPoint(referenceNormal, referenceImagePlane.imagePositionPatient);
var originDirection = referencePlane.clone().intersectPlane(targetPlane);
var origin = originDirection.origin;
var direction = originDirection.direction;
// Calculate the longest possible length in the reference image plane (the length of the diagonal)
var bottomRight = imagePointToPatientPoint({
x: referenceImagePlane.columns,
y: referenceImagePlane.rows
}, referenceImagePlane);
var distance = referenceImagePlane.imagePositionPatient.distanceTo(bottomRight);
// Use this distance to bound the ray intersecting the two planes
var line = new cornerstoneMath.Line3();
line.start = origin;
line.end = origin.clone().add(direction.multiplyScalar(distance));
// Find the intersections between this line and the reference image plane's four sides
var rect = getRectangleFromImagePlane(referenceImagePlane);
var intersections = lineRectangleIntersection(line, rect);
// Return the intersections between this line and the reference image plane's sides
// in order to draw the reference line from the target image.
if (intersections.length !== 2) {
return;
}
var points = {
start: intersections[0],
end: intersections[1]
};
return points;
}
// module/private exports
cornerstoneTools.projectPatientPointToImagePlane = projectPatientPointToImagePlane;
cornerstoneTools.imagePointToPatientPoint = imagePointToPatientPoint;
cornerstoneTools.planePlaneIntersection = planePlaneIntersection;
})($, cornerstone, cornerstoneTools);
// End Source; src/util/pointProjector.js
// Begin Source: src/util/scroll.js
(function(cornerstone, cornerstoneTools) {
'use strict';
function scroll(element, images) {
var toolData = cornerstoneTools.getToolState(element, 'stack');
if (toolData === undefined || toolData.data === undefined || toolData.data.length === 0) {
return;
}
var stackData = toolData.data[0];
var newImageIdIndex = stackData.currentImageIdIndex + images;
newImageIdIndex = Math.min(stackData.imageIds.length - 1, newImageIdIndex);
newImageIdIndex = Math.max(0, newImageIdIndex);
cornerstoneTools.scrollToIndex(element, newImageIdIndex);
}
// module exports
cornerstoneTools.scroll = scroll;
})(cornerstone, cornerstoneTools);
// End Source; src/util/scroll.js
// Begin Source: src/util/scrollToIndex.js
(function(cornerstone, cornerstoneTools) {
'use strict';
function scrollToIndex(element, newImageIdIndex) {
var toolData = cornerstoneTools.getToolState(element, 'stack');
if (toolData === undefined || toolData.data === undefined || toolData.data.length === 0) {
return;
}
var stackData = toolData.data[0];
// Allow for negative indexing
if (newImageIdIndex < 0) {
newImageIdIndex += stackData.imageIds.length;
}
var startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler();
var endLoadingHandler = cornerstoneTools.loadHandlerManager.getEndLoadHandler();
var errorLoadingHandler = cornerstoneTools.loadHandlerManager.getErrorLoadingHandler();
function doneCallback(image) {
//console.log('interaction done: ' + image.imageId);
if (stackData.currentImageIdIndex === newImageIdIndex) {
cornerstone.displayImage(element, image, viewport);
if (endLoadingHandler) {
endLoadingHandler(element);
}
}
}
function failCallback(error) {
var imageId = stackData.imageIds[newImageIdIndex];
if (errorLoadingHandler) {
errorLoadingHandler(element, imageId, error);
}
}
if (newImageIdIndex === stackData.currentImageIdIndex) {
return;
}
if (startLoadingHandler) {
startLoadingHandler(element);
}
var eventData = {
newImageIdIndex: newImageIdIndex,
direction: newImageIdIndex - stackData.currentImageIdIndex
};
stackData.currentImageIdIndex = newImageIdIndex;
var viewport = cornerstone.getViewport(element);
var newImageId = stackData.imageIds[newImageIdIndex];
// Retry image loading in cases where previous image promise
// was rejected, if the option is set
var config = cornerstoneTools.stackScroll.getConfiguration();
if (config && config.retryLoadOnScroll === true) {
var newImagePromise = cornerstone.imageCache.getImagePromise(newImageId);
if (newImagePromise && newImagePromise.state() === 'rejected') {
cornerstone.imageCache.removeImagePromise(newImageId);
}
}
var requestPoolManager = cornerstoneTools.requestPoolManager;
var type = 'interaction';
cornerstoneTools.requestPoolManager.clearRequestStack(type);
requestPoolManager.addRequest(element, newImageId, type, doneCallback, failCallback);
requestPoolManager.startGrabbing();
$(element).trigger('CornerstoneStackScroll', eventData);
}
// module exports
cornerstoneTools.scrollToIndex = scrollToIndex;
cornerstoneTools.loadHandlers = {};
})(cornerstone, cornerstoneTools);
// End Source; src/util/scrollToIndex.js
// Begin Source: src/util/setContextToDisplayFontSize.js
/**
* This module sets the transformation matrix for a canvas context so it displays fonts
* smoothly even when the image is highly scaled up
*/
(function(cornerstone, cornerstoneTools) {
'use strict';
/**
* Sets the canvas context transformation matrix so it is scaled to show text
* more cleanly even if the image is scaled up. See
* https://github.com/chafey/cornerstoneTools/wiki/DrawingText
* for more information
*
* @param ee
* @param context
* @param fontSize
* @returns {{fontSize: number, lineHeight: number, fontScale: number}}
*/
function setContextToDisplayFontSize(ee, context, fontSize) {
var fontScale = 0.1;
cornerstone.setToPixelCoordinateSystem(ee, context, fontScale);
// return the font size to use
var scaledFontSize = fontSize / ee.viewport.scale / fontScale;
// TODO: actually calculate this?
var lineHeight = fontSize / ee.viewport.scale / fontScale;
return {
fontSize: scaledFontSize, lineHeight: lineHeight, fontScale: fontScale
};
}
// Module exports
cornerstoneTools.setContextToDisplayFontSize = setContextToDisplayFontSize;
})(cornerstone, cornerstoneTools);
// End Source; src/util/setContextToDisplayFontSize.js