Work on refactoring measurements out of Lesion Tracker package
- Removed AssociatedStudies Collection - Measurement API is generated from configuration files - Data exchange methods are defined in configuration
This commit is contained in:
1 parent
c511edd419
commit
ab4d4c9008
537 files changed
+5122
-3545
No files matched your search
@@ -1,28 +0,0 @@
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
applyWLPreset = function(presetName, element) {
|
||||
log.info("Applying WL Preset: " + presetName);
|
||||
var viewport = cornerstone.getViewport(element);
|
||||
|
||||
if (presetName === 'Default') {
|
||||
var enabledElement = cornerstone.getEnabledElement(element);
|
||||
viewport.voi.windowWidth = enabledElement.image.windowWidth;
|
||||
viewport.voi.windowCenter = enabledElement.image.windowCenter;
|
||||
} else {
|
||||
var preset = OHIF.viewer.wlPresets[presetName];
|
||||
viewport.voi.windowWidth = preset.ww;
|
||||
viewport.voi.windowCenter = preset.wc;
|
||||
}
|
||||
|
||||
// Update the viewport
|
||||
cornerstone.setViewport(element, viewport);
|
||||
};
|
||||
|
||||
applyWLPresetToActiveElement = function(presetName) {
|
||||
var element = getActiveViewportElement();
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
applyWLPreset(presetName, element);
|
||||
};
|
||||
@@ -1,123 +0,0 @@
|
||||
/**
|
||||
* Creates a set of series to be placed in the Study Browser
|
||||
* The series that appear in the Study Browser must represent
|
||||
* imaging modalities.
|
||||
*
|
||||
* Furthermore, for drag/drop functionality,
|
||||
* it is easiest if the stack objects also contain information about
|
||||
* which study they are linked to.
|
||||
*
|
||||
* @param study The study instance to be used
|
||||
* @returns {Array} An array of series to be placed in the Study Browser
|
||||
*/
|
||||
createStacks = function(study) {
|
||||
// Define an empty array of display sets
|
||||
var displaySets = [];
|
||||
|
||||
if (!study || !study.seriesList) {
|
||||
return displaySets;
|
||||
}
|
||||
|
||||
study.seriesList.forEach(series => {
|
||||
// If the series has no instances, skip it
|
||||
if (!series.instances) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Search through the instances of this series
|
||||
// Split Multi-frame instances and Single-image modalities
|
||||
// into their own specific display sets. Place the rest of each
|
||||
// series into another display set.
|
||||
let stackableInstances = [];
|
||||
series.instances.forEach(instance => {
|
||||
// All imaging modalities must have a valid value for sopClassUid or rows
|
||||
if (!isImage(instance.sopClassUid) && !instance.rows) {
|
||||
return;
|
||||
}
|
||||
|
||||
let displaySet;
|
||||
if (isMultiFrame(instance)) {
|
||||
displaySet = makeDisplaySet(series, [ instance ]);
|
||||
displaySet.isClip = true;
|
||||
|
||||
// Include the study instance Uid for drag/drop purposes
|
||||
displaySet.studyInstanceUid = study.studyInstanceUid;
|
||||
|
||||
// Override the default value of instances.length
|
||||
displaySet.numImageFrames = instance.numFrames;
|
||||
|
||||
// Include the instance number
|
||||
displaySet.instanceNumber = instance.instanceNumber;
|
||||
|
||||
// Include the acquisition datetime
|
||||
displaySet.acquisitionDatetime = instance.acquisitionDatetime;
|
||||
|
||||
displaySets.push(displaySet);
|
||||
} else if (isSingleImageModality(instance.modality)) {
|
||||
displaySet = makeDisplaySet(series, [ instance ]);
|
||||
|
||||
// Include the study instance Uid
|
||||
displaySet.studyInstanceUid = study.studyInstanceUid;
|
||||
|
||||
// Include the instance number
|
||||
displaySet.instanceNumber = instance.instanceNumber;
|
||||
|
||||
// Include the acquisition datetime
|
||||
displaySet.acquisitionDatetime = instance.acquisitionDatetime;
|
||||
|
||||
displaySets.push(displaySet);
|
||||
} else {
|
||||
stackableInstances.push(instance);
|
||||
}
|
||||
});
|
||||
|
||||
if (stackableInstances.length) {
|
||||
let displaySet = makeDisplaySet(series, stackableInstances);
|
||||
displaySet.studyInstanceUid = study.studyInstanceUid;
|
||||
displaySets.push(displaySet);
|
||||
}
|
||||
});
|
||||
|
||||
return displaySets;
|
||||
};
|
||||
|
||||
function makeDisplaySet(series, instances) {
|
||||
const instance = instances[0];
|
||||
|
||||
let displaySet = {
|
||||
seriesInstanceUid: series.seriesInstanceUid,
|
||||
seriesNumber: series.seriesNumber,
|
||||
seriesDescription: series.seriesDescription,
|
||||
numImageFrames: instances.length,
|
||||
frameRate: instance.frameTime,
|
||||
images: instances,
|
||||
modality: series.modality,
|
||||
isMultiFrame: isMultiFrame(instance)
|
||||
};
|
||||
|
||||
// Sort the images in this series
|
||||
displaySet.images.sort(function(a, b) {
|
||||
if (a.instanceNumber && b.instanceNumber &&
|
||||
a.instanceNumber !== b.instanceNumber) {
|
||||
return a.instanceNumber - b.instanceNumber;
|
||||
}
|
||||
});
|
||||
|
||||
// Include the first image instance number
|
||||
displaySet.instanceNumber = displaySet.images[0].instanceNumber;
|
||||
|
||||
// Create a unique ID for this stack so we can reference it
|
||||
displaySet.displaySetInstanceUid = Random.id();
|
||||
|
||||
return displaySet;
|
||||
}
|
||||
|
||||
function isSingleImageModality(modality) {
|
||||
return (modality === 'CR' ||
|
||||
modality === 'MG' ||
|
||||
modality === 'DX');
|
||||
}
|
||||
|
||||
function isMultiFrame(instance) {
|
||||
return instance.numFrames > 1;
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
Meteor.startup(function() {
|
||||
var debug = false;
|
||||
|
||||
if (debug === true) {
|
||||
// http://www.meteorpedia.com/read/Debugging_Reactivity
|
||||
|
||||
Meteor.autorun(function(computation) {
|
||||
computation.onInvalidate(function() {
|
||||
console.trace();
|
||||
});
|
||||
});
|
||||
|
||||
var wrappedFind = Meteor.Collection.prototype.find;
|
||||
|
||||
Meteor.Collection.prototype.find = function() {
|
||||
var cursor = wrappedFind.apply(this, arguments);
|
||||
var collectionName = this._name || this._debugName;
|
||||
|
||||
/*cursor.observeChanges({
|
||||
added: function(id, fields) {
|
||||
console.log(collectionName, 'added', id, fields);
|
||||
},
|
||||
changed: function(id, fields) {
|
||||
console.log(collectionName, 'changed', id, fields);
|
||||
},
|
||||
movedBefore: function(id, before) {
|
||||
console.log(collectionName, 'movedBefore', id, before);
|
||||
},
|
||||
removed: function(id) {
|
||||
console.log(collectionName, 'removed', id);
|
||||
}
|
||||
});*/
|
||||
|
||||
cursor.observe({
|
||||
added: function(data) {
|
||||
console.log(collectionName, 'added', data);
|
||||
},
|
||||
changed: function(data) {
|
||||
console.log(collectionName, 'changed', data);
|
||||
},
|
||||
removed: function(data) {
|
||||
console.log(collectionName, 'removed', data);
|
||||
}
|
||||
});
|
||||
|
||||
return cursor;
|
||||
};
|
||||
|
||||
function logRenders() {
|
||||
Object.keys(Template).forEach(function(name) {
|
||||
if (name.indexOf('_') > -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var template = Template[name];
|
||||
var oldRender = template.rendered;
|
||||
var counter = 0;
|
||||
|
||||
template.rendered = function() {
|
||||
console.log(name, 'render count: ', ++counter);
|
||||
oldRender && oldRender.apply(this, arguments);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
logRenders();
|
||||
}
|
||||
});
|
||||
@@ -1,43 +0,0 @@
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
/**
|
||||
* This function disables reference lines for a specific viewport element.
|
||||
* It also enables reference lines for all other viewports with the
|
||||
* class .imageViewerViewport.
|
||||
*
|
||||
* @param element {node} DOM Node representing the viewport element
|
||||
*/
|
||||
displayReferenceLines = element => {
|
||||
log.info("imageViewerViewport displayReferenceLines");
|
||||
|
||||
// Check if image plane (orientation / loction) data is present for the current image
|
||||
const enabledElement = cornerstone.getEnabledElement(element);
|
||||
const imageId = enabledElement.image.imageId;
|
||||
const imagePlane = cornerstoneTools.metaData.get('imagePlane', imageId);
|
||||
|
||||
if (!OHIF.viewer.refLinesEnabled || !imagePlane || !imagePlane.frameOfReferenceUID) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable reference lines for the current element
|
||||
cornerstoneTools.referenceLines.tool.disable(element);
|
||||
|
||||
// Loop through all other viewport elements and enable reference lines
|
||||
$('.imageViewerViewport').not(element).each((index, viewportElement) => {
|
||||
let imageId;
|
||||
if($(viewportElement).find('canvas').length) {
|
||||
try {
|
||||
const enabledElement = cornerstone.getEnabledElement(viewportElement);
|
||||
imageId = enabledElement.image.imageId;
|
||||
} catch(error) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!imageId) {
|
||||
return;
|
||||
}
|
||||
|
||||
cornerstoneTools.referenceLines.tool.enable(viewportElement, OHIF.viewer.updateImageSynchronizer);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -1,309 +0,0 @@
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
Meteor.startup(function() {
|
||||
|
||||
if (!OHIF.viewer) {
|
||||
OHIF.viewer = {};
|
||||
}
|
||||
|
||||
OHIF.viewer.defaultHotkeys = {
|
||||
defaultTool: 'ESC',
|
||||
angle: 'A',
|
||||
stackScroll: 'S',
|
||||
pan: 'P',
|
||||
magnify: 'M',
|
||||
scrollDown: 'DOWN',
|
||||
scrollUp: 'UP',
|
||||
nextDisplaySet: 'PAGEDOWN',
|
||||
previousDisplaySet: 'PAGEUP',
|
||||
nextPanel: 'RIGHT',
|
||||
previousPanel: 'LEFT',
|
||||
invert: 'I',
|
||||
flipV: 'V',
|
||||
flipH: 'H',
|
||||
wwwc: 'W',
|
||||
zoom: 'Z',
|
||||
cinePlay: 'SPACE',
|
||||
rotateR: 'R',
|
||||
rotateL: 'L',
|
||||
toggleOverlayTags: 'SHIFT',
|
||||
WLPresetSoftTissue: ['NUMPAD1', '1'],
|
||||
WLPresetLung: ['NUMPAD2', '2'],
|
||||
WLPresetLiver: ['NUMPAD3', '3'],
|
||||
WLPresetBone: ['NUMPAD4', '4'],
|
||||
WLPresetBrain: ['NUMPAD5', '5']
|
||||
};
|
||||
|
||||
// For now
|
||||
OHIF.viewer.hotkeys = OHIF.viewer.defaultHotkeys;
|
||||
|
||||
OHIF.viewer.defaultWLPresets = {
|
||||
SoftTissue: {
|
||||
wc: 40,
|
||||
ww: 400
|
||||
},
|
||||
Lung: {
|
||||
wc: -600,
|
||||
ww: 1500
|
||||
},
|
||||
Liver: {
|
||||
wc: 90,
|
||||
ww: 150
|
||||
},
|
||||
Bone: {
|
||||
wc: 480,
|
||||
ww: 2500
|
||||
},
|
||||
Brain: {
|
||||
wc: 40,
|
||||
ww: 80
|
||||
}
|
||||
};
|
||||
|
||||
// For now
|
||||
OHIF.viewer.wlPresets = OHIF.viewer.defaultWLPresets;
|
||||
|
||||
OHIF.viewer.hotkeyFunctions = {
|
||||
wwwc: function() {
|
||||
toolManager.setActiveTool('wwwc');
|
||||
},
|
||||
zoom: function() {
|
||||
toolManager.setActiveTool('zoom');
|
||||
},
|
||||
angle: function() {
|
||||
toolManager.setActiveTool('angle');
|
||||
},
|
||||
dragProbe: function() {
|
||||
toolManager.setActiveTool('dragProbe');
|
||||
},
|
||||
ellipticalRoi: function() {
|
||||
toolManager.setActiveTool('ellipticalRoi');
|
||||
},
|
||||
magnify: function() {
|
||||
toolManager.setActiveTool('magnify');
|
||||
},
|
||||
annotate: function() {
|
||||
toolManager.setActiveTool('annotate');
|
||||
},
|
||||
stackScroll: function() {
|
||||
toolManager.setActiveTool('stackScroll');
|
||||
},
|
||||
pan: function() {
|
||||
toolManager.setActiveTool('pan');
|
||||
},
|
||||
length: function() {
|
||||
toolManager.setActiveTool('length');
|
||||
},
|
||||
spine: function() {
|
||||
toolManager.setActiveTool('spine');
|
||||
},
|
||||
wwwcRegion: function() {
|
||||
toolManager.setActiveTool('wwwcRegion');
|
||||
},
|
||||
zoomIn: function() {
|
||||
var button = document.getElementById('zoomIn');
|
||||
flashButton(button);
|
||||
zoomIn();
|
||||
},
|
||||
zoomOut: function() {
|
||||
var button = document.getElementById('zoomOut');
|
||||
flashButton(button);
|
||||
zoomOut();
|
||||
},
|
||||
zoomToFit: function() {
|
||||
var button = document.getElementById('zoomToFit');
|
||||
flashButton(button);
|
||||
zoomToFit();
|
||||
},
|
||||
scrollDown: function() {
|
||||
var container = $('.viewportContainer.active');
|
||||
var button = container.find('#nextImage').get(0);
|
||||
|
||||
if (!container.find('.imageViewerViewport').hasClass('empty')) {
|
||||
flashButton(button);
|
||||
switchToImageRelative(1);
|
||||
}
|
||||
},
|
||||
scrollFirstImage: function() {
|
||||
var container = $('.viewportContainer.active');
|
||||
if (!container.find('.imageViewerViewport').hasClass('empty')) {
|
||||
switchToImageByIndex(0);
|
||||
}
|
||||
},
|
||||
scrollLastImage: function() {
|
||||
var container = $('.viewportContainer.active');
|
||||
if (!container.find('.imageViewerViewport').hasClass('empty')) {
|
||||
switchToImageByIndex(-1);
|
||||
}
|
||||
},
|
||||
scrollUp: function() {
|
||||
var container = $('.viewportContainer.active');
|
||||
if (!container.find('.imageViewerViewport').hasClass('empty')) {
|
||||
var button = container.find('#prevImage').get(0);
|
||||
flashButton(button);
|
||||
switchToImageRelative(-1);
|
||||
}
|
||||
},
|
||||
previousDisplaySet() {
|
||||
OHIF.viewer.moveDisplaySets(false);
|
||||
},
|
||||
nextDisplaySet() {
|
||||
OHIF.viewer.moveDisplaySets(true);
|
||||
},
|
||||
nextPanel: function() {
|
||||
nextActivePanel();
|
||||
},
|
||||
previousPanel: function() {
|
||||
previousActivePanel();
|
||||
},
|
||||
invert: function() {
|
||||
var button = document.getElementById('invert');
|
||||
flashButton(button);
|
||||
invert();
|
||||
},
|
||||
flipV: function() {
|
||||
var button = document.getElementById('flipV');
|
||||
flashButton(button);
|
||||
flipV();
|
||||
},
|
||||
flipH: function() {
|
||||
var button = document.getElementById('flipH');
|
||||
flashButton(button);
|
||||
flipH();
|
||||
},
|
||||
rotateR: function() {
|
||||
var button = document.getElementById('rotateR');
|
||||
flashButton(button);
|
||||
rotateR();
|
||||
},
|
||||
rotateL: function() {
|
||||
var button = document.getElementById('rotateL');
|
||||
flashButton(button);
|
||||
rotateL();
|
||||
},
|
||||
cinePlay: function() {
|
||||
toggleCinePlay();
|
||||
},
|
||||
defaultTool: function() {
|
||||
var tool = toolManager.getDefaultTool();
|
||||
toolManager.setActiveTool(tool);
|
||||
},
|
||||
toggleOverlayTags: function() {
|
||||
var dicomTags = $('.imageViewerViewportOverlay .dicomTag');
|
||||
if (dicomTags.eq(0).css('display') === 'none') {
|
||||
dicomTags.show();
|
||||
} else {
|
||||
dicomTags.hide();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
OHIF.viewer.loadedSeriesData = {};
|
||||
});
|
||||
|
||||
// Define a jQuery reverse function
|
||||
$.fn.reverse = [].reverse;
|
||||
|
||||
function previousActivePanel() {
|
||||
log.info('nextActivePanel');
|
||||
var currentIndex = Session.get('activeViewport');
|
||||
currentIndex--;
|
||||
|
||||
var viewports = $('.imageViewerViewport');
|
||||
var numViewports = viewports.length;
|
||||
if (currentIndex < 0) {
|
||||
currentIndex = numViewports - 1;
|
||||
}
|
||||
|
||||
var element = viewports.get(currentIndex);
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
setActiveViewport(element);
|
||||
}
|
||||
|
||||
function nextActivePanel() {
|
||||
log.info('nextActivePanel');
|
||||
var currentIndex = Session.get('activeViewport');
|
||||
currentIndex++;
|
||||
|
||||
var viewports = $('.imageViewerViewport');
|
||||
var numViewports = viewports.length;
|
||||
if (currentIndex >= numViewports) {
|
||||
currentIndex = 0;
|
||||
}
|
||||
|
||||
var element = viewports.get(currentIndex);
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
setActiveViewport(element);
|
||||
}
|
||||
|
||||
function flashButton(button) {
|
||||
if (!button) {
|
||||
return;
|
||||
}
|
||||
|
||||
button.classList.add('active');
|
||||
setTimeout(function() {
|
||||
button.classList.remove('active');
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function bindHotkey(hotkey, task) {
|
||||
var hotkeyFunctions = OHIF.viewer.hotkeyFunctions;
|
||||
|
||||
// Only bind defined, non-empty HotKeys
|
||||
if (!hotkey || hotkey === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
var fn;
|
||||
if (task.indexOf('WLPreset') > -1) {
|
||||
var presetName = task.replace('WLPreset', '');
|
||||
fn = function() {
|
||||
applyWLPresetToActiveElement(presetName);
|
||||
};
|
||||
} else {
|
||||
fn = hotkeyFunctions[task];
|
||||
|
||||
// If the function doesn't exist in the
|
||||
// hotkey function list, try the viewer-specific function list
|
||||
if (!fn && OHIF.viewer && OHIF.viewer.functionList) {
|
||||
fn = OHIF.viewer.functionList[task];
|
||||
}
|
||||
}
|
||||
|
||||
if (!fn) {
|
||||
return;
|
||||
}
|
||||
|
||||
var hotKeyForBinding = hotkey.toLowerCase();
|
||||
|
||||
$(document).bind('keydown', hotKeyForBinding, fn);
|
||||
}
|
||||
|
||||
enableHotkeys = function() {
|
||||
var viewerHotkeys = OHIF.viewer.hotkeys;
|
||||
|
||||
$(document).unbind('keydown');
|
||||
|
||||
Object.keys(viewerHotkeys).forEach(function(task) {
|
||||
var taskHotkeys = viewerHotkeys[task];
|
||||
if (!taskHotkeys || !taskHotkeys.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (taskHotkeys.constructor === Array) {
|
||||
taskHotkeys.forEach(function(hotkey) {
|
||||
bindHotkey(hotkey, task);
|
||||
});
|
||||
} else {
|
||||
// taskHotkeys represents a single key
|
||||
bindHotkey(taskHotkeys, task);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* This function enables stack prefetching for a specified element (viewport)
|
||||
* It first disables any prefetching currently occurring on any other viewports.
|
||||
*
|
||||
* @param element {node} DOM Node representing the viewport element
|
||||
*/
|
||||
enablePrefetchOnElement = function(element) {
|
||||
log.info("imageViewerViewport enablePrefetchOnElement");
|
||||
|
||||
// Loop through all viewports and disable stackPrefetch
|
||||
$('.imageViewerViewport').each(function() {
|
||||
if (!$(this).find('canvas').length) {
|
||||
return;
|
||||
}
|
||||
cornerstoneTools.stackPrefetch.disable(this);
|
||||
});
|
||||
|
||||
// Make sure there is a stack to fetch
|
||||
const stack = cornerstoneTools.getToolState(element, 'stack');
|
||||
if (stack && stack.data.length && stack.data[0].imageIds.length > 1) {
|
||||
cornerstoneTools.stackPrefetch.enable(element);
|
||||
}
|
||||
};
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Obtain an imageId for Cornerstone from an image instance
|
||||
*
|
||||
* @param instance
|
||||
* @returns {string} The imageId to be used by Cornerstone
|
||||
*/
|
||||
getImageId = function(instance, frame) {
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (instance.url) {
|
||||
return instance.url;
|
||||
}
|
||||
|
||||
if (instance.wadouri) {
|
||||
var imageId = 'dicomweb:' + instance.wadouri; // WADO-URI;
|
||||
if (frame !== undefined) {
|
||||
imageId += '&frame=' + frame;
|
||||
}
|
||||
|
||||
return imageId;
|
||||
} else {
|
||||
// TODO= Check multiframe image support with WADO-RS
|
||||
return getWADORSImageId(instance); // WADO-RS Retrieve Frame
|
||||
}
|
||||
};
|
||||
@@ -1,60 +0,0 @@
|
||||
/**
|
||||
* Obtain an imageId for Cornerstone based on the WADO-RS scheme
|
||||
*
|
||||
* @param instance
|
||||
* @returns {string} The imageId to be used by Cornerstone
|
||||
*/
|
||||
|
||||
getWADORSImageId = function(instance) {
|
||||
var columnPixelSpacing = 1.0;
|
||||
var rowPixelSpacing = 1.0;
|
||||
if (instance.pixelSpacing) {
|
||||
var split = instance.pixelSpacing.split('\\');
|
||||
rowPixelSpacing = parseFloat(split[0]);
|
||||
columnPixelSpacing = parseFloat(split[1]);
|
||||
}
|
||||
|
||||
var windowWidth;
|
||||
var windowCenter;
|
||||
|
||||
if (instance.windowWidth && instance.windowCenter) {
|
||||
windowWidth = parseFloat(instance.windowWidth.split('\\')[0]);
|
||||
windowCenter = parseFloat(instance.windowCenter.split('\\')[0]);
|
||||
}
|
||||
|
||||
var image = {
|
||||
uri: instance.wadorsuri,
|
||||
//imageId : '',
|
||||
//minPixelValue : 0,
|
||||
//maxPixelValue : 255,
|
||||
slope: instance.rescaleSlope,
|
||||
intercept: instance.rescaleIntercept,
|
||||
samplesPerPixel: instance.samplesPerPixel,
|
||||
imageOrientationPatient: instance.imageOrientationPatient,
|
||||
imagePositionPatient: instance.imagePositionPatient,
|
||||
sopClassUid: instance.sopClassUid,
|
||||
instanceNumber: instance.instanceNumber,
|
||||
frameOfReferenceUID: instance.frameOfReferenceUID,
|
||||
windowCenter: windowCenter,
|
||||
windowWidth: windowWidth,
|
||||
//render: cornerstone.renderColorImage,
|
||||
//getPixelData: getPixelData,
|
||||
//getImageData: getImageData,
|
||||
//getCanvas: getCanvas,
|
||||
rows: instance.rows,
|
||||
columns: instance.columns,
|
||||
height: instance.rows,
|
||||
width: instance.columns,
|
||||
color: false,
|
||||
columnPixelSpacing: columnPixelSpacing,
|
||||
rowPixelSpacing: rowPixelSpacing,
|
||||
invert: false,
|
||||
sizeInBytes: instance.rows * instance.columns * (instance.bitsAllocated / 8),
|
||||
instance: instance
|
||||
};
|
||||
|
||||
var imageId = cornerstoneWADOImageLoader.imageManager.add(image);
|
||||
|
||||
log.info('WADO-RS ImageID: ' + imageId);
|
||||
return imageId;
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* A global Blaze UI helper to capitalizes the first letter of an input String
|
||||
*
|
||||
* Credit to:
|
||||
*
|
||||
* http://stackoverflow.com/questions/1026069/capitalize-the-first-letter-of-string-in-javascript
|
||||
*/
|
||||
UI.registerHelper('capitalizeFirstLetter', function (context) {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
return context.charAt(0).toUpperCase() + context.slice(1);
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* A global Blaze UI helper function to format DICOM Dates using the Moment library
|
||||
*/
|
||||
UI.registerHelper('formatDA', function (context, format, options) {
|
||||
if (!context) {
|
||||
return undefined;
|
||||
}
|
||||
var dateAsMoment = moment(context, "YYYYMMDD");
|
||||
var strFormat = "MMM D, YYYY";
|
||||
if (options) {
|
||||
strFormat = format;
|
||||
}
|
||||
return dateAsMoment.format(strFormat);
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
/**
|
||||
* A global Blaze UI helper function to format JavaScript Dates using the Moment library
|
||||
*/
|
||||
UI.registerHelper('formatJSDate', function(context, format, options) {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
var dateAsMoment = moment(new Date(context));
|
||||
var strFormat = 'MMM D, YYYY';
|
||||
if (options) {
|
||||
strFormat = format;
|
||||
}
|
||||
|
||||
return dateAsMoment.format(strFormat);
|
||||
});
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* A global Blaze UI helper to format a float value to a specified precision
|
||||
*/
|
||||
UI.registerHelper('formatNumberPrecision', function(context, precision) {
|
||||
if (context) {
|
||||
return parseFloat(context).toFixed(precision);
|
||||
}
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* Formats a patient name for display purposes
|
||||
*/
|
||||
formatPN = function(context) {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
return context.replace('^', ', ');
|
||||
};
|
||||
|
||||
/**
|
||||
* A global Blaze UI helper to format a patient name for display purposes
|
||||
*/
|
||||
UI.registerHelper('formatPN', formatPN);
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* A global Blaze UI helper to format a DICOM Time for display using the Moment library
|
||||
*/
|
||||
UI.registerHelper('formatTM', function(context, options) {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
// DICOM Time is stored as HHmmss.SSS, where:
|
||||
// HH 24 hour time:
|
||||
// m mm 0..59 Minutes
|
||||
// s ss 0..59 Seconds
|
||||
// S SS SSS 0..999 Fractional seconds
|
||||
//
|
||||
// See MomentJS: http://momentjs.com/docs/#/parsing/string-format/
|
||||
var dateTime = moment(context, 'HHmmss.SSS');
|
||||
|
||||
var format = "HH:mm:ss";
|
||||
if (options && options.format) {
|
||||
format = options.format;
|
||||
}
|
||||
|
||||
return dateTime.format(format);
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Helper for retrieving username given userId
|
||||
*/
|
||||
UI.registerHelper('getUsername', function(userId) {
|
||||
var user = Meteor.users.findOne({
|
||||
userId: userId
|
||||
});
|
||||
|
||||
if (user && user.name) {
|
||||
return user.name;
|
||||
}
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Helper for checking datatype of a variable
|
||||
*/
|
||||
UI.registerHelper('ifTypeIs', function(value, match, attributeName) {
|
||||
if (typeof(value) === match) {
|
||||
return attributeName;
|
||||
}
|
||||
|
||||
return '';
|
||||
});
|
||||
@@ -1,3 +0,0 @@
|
||||
UI.registerHelper('inc', function(value) {
|
||||
return parseInt(value) + 1;
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Helper for setting checkboxes as checked or unchecked inside templates,
|
||||
* based on another variable's value
|
||||
*/
|
||||
UI.registerHelper("inlineIf", function (value, match, attributeName) {
|
||||
if(value === match) {
|
||||
return attributeName;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
@@ -1,49 +0,0 @@
|
||||
/**
|
||||
* Boolean helper to identify if a series instance is active in some viewport
|
||||
*/
|
||||
Template.registerHelper('isDisplaySetActive', (displaySetInstanceUid, viewportIndex) => {
|
||||
// Run this computation every time the viewports are updated
|
||||
Session.get('LayoutManagerUpdated');
|
||||
|
||||
// Stop here if layoutManager is not defined yet
|
||||
if (!window.layoutManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the display set is current visible in any of the layout
|
||||
// manager's displayed viewports. Note that we have to check the
|
||||
// onscreen number of viewports here, since the layout manager will
|
||||
// keep the viewport data of old viewports, even after the layout is changed.
|
||||
//
|
||||
// This behaviour is intentional. If the user displays four viewports, then assigns
|
||||
// display sets to them, and then switches to / from another layout configuration,
|
||||
// we don't want them to lose their specified viewports.
|
||||
let result = false;
|
||||
if (_.isUndefined(viewportIndex)) {
|
||||
// Get the number of viewports that are currently displayed
|
||||
// (Note, viewportData may have more entries!)
|
||||
const currentNumberOfViewports = layoutManager.getNumberOfViewports();
|
||||
|
||||
// Loop through the viewport data up until the currently displayed
|
||||
// number of viewports
|
||||
let viewportData = window.layoutManager.viewportData;
|
||||
for (let i = 0; i < currentNumberOfViewports; i++) {
|
||||
const data = viewportData[i];
|
||||
|
||||
// If the display set is displayed in this viewport, stop here
|
||||
if (data && data.displaySetInstanceUid === displaySetInstanceUid) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const data = window.layoutManager.viewportData[viewportIndex];
|
||||
|
||||
// If the display set is displayed in this viewport, stop here
|
||||
if (data && data.displaySetInstanceUid === displaySetInstanceUid) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Helper function to determine if the current client devices
|
||||
* is touch-capable. This can be used to modify certain aspects of the UI.
|
||||
*
|
||||
* The check may not work on all devices!
|
||||
*
|
||||
* @returns {boolean} true if the client device is touch-capable, false otherwise
|
||||
*/
|
||||
isTouchDevice = function() {
|
||||
return (('ontouchstart' in window) ||
|
||||
(navigator.MaxTouchPoints > 0) ||
|
||||
(navigator.msMaxTouchPoints > 0));
|
||||
};
|
||||
|
||||
/**
|
||||
* Blaze helper for checking if the current device is touch capable
|
||||
*
|
||||
* @returns {boolean} true if the client device is touch-capable, false otherwise
|
||||
*/
|
||||
UI.registerHelper('isTouchDevice', isTouchDevice);
|
||||
@@ -1,13 +0,0 @@
|
||||
/**
|
||||
* A global Blaze UI helper function to format JavaScript Dates using the Moment library
|
||||
*/
|
||||
UI.registerHelper('jsDateFromNow', function(context, format, options) {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
Session.get('timeAgoVariable');
|
||||
|
||||
var dateAsMoment = moment(new Date(context));
|
||||
return dateAsMoment.fromNow();
|
||||
});
|
||||
@@ -1,6 +0,0 @@
|
||||
UI.registerHelper('objectEach', function(object) {
|
||||
// http://stackoverflow.com/questions/30234732/how-to-print-key-and-values-in-meteor-template
|
||||
return _.map(object, function(value, key) {
|
||||
return _.extend({key: key}, value);
|
||||
});
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
UI.registerHelper('objectToPairs', function(object) {
|
||||
// http://stackoverflow.com/questions/30234732/how-to-print-key-and-values-in-meteor-template
|
||||
return _.map(object, function(value, key) {
|
||||
return {
|
||||
key: key,
|
||||
value: value
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* A global Blaze UI helper to Stringify a JavaScript object
|
||||
*
|
||||
* Credit to:
|
||||
*
|
||||
* http://stackoverflow.com/questions/1026069/capitalize-the-first-letter-of-string-in-javascript
|
||||
*/
|
||||
UI.registerHelper('prettyPrintStringify', function(context) {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
var string = JSON.stringify(context, null, 2);
|
||||
string = string.replace(/['"]+/g, '');
|
||||
string = string.replace('{', '');
|
||||
string = string.replace('}', '');
|
||||
string = string.replace(',', '\n');
|
||||
return string;
|
||||
});
|
||||
@@ -1,57 +0,0 @@
|
||||
import { _ } from 'meteor/underscore';
|
||||
import { Template } from 'meteor/templating';
|
||||
|
||||
/**
|
||||
* Global Blaze UI helper to sort array elements
|
||||
* by an array element's property (property) or deep object property (property.childProperty)
|
||||
* Sorts ascending as default
|
||||
*/
|
||||
Template.registerHelper('sort', (array, sortBy, sortType) => {
|
||||
if (!sortBy) {
|
||||
return array;
|
||||
}
|
||||
|
||||
// To keep the order for the same values of the field which is used to sort:
|
||||
// 1. Group the array by the field
|
||||
// 2. Sort the grouped array
|
||||
// 3. Ungroup the sorted array
|
||||
|
||||
const groupedArray = _.groupBy(array, (element) => {
|
||||
if (sortBy) {
|
||||
var groupingElement = getKeyValue(element, sortBy);
|
||||
if (groupingElement) {
|
||||
return groupingElement;
|
||||
}
|
||||
}
|
||||
return element;
|
||||
});
|
||||
|
||||
const sortedArray = _.sortBy(groupedArray, (element) => {
|
||||
if (sortBy) {
|
||||
var sortingElement = getKeyValue(element[0], sortBy);
|
||||
if (sortingElement) {
|
||||
return sortingElement;
|
||||
}
|
||||
}
|
||||
return element;
|
||||
});
|
||||
|
||||
if (sortType === 'desc') {
|
||||
return _.flatten(sortedArray.reverse(), true);
|
||||
}
|
||||
|
||||
return _.flatten(sortedArray, true);
|
||||
});
|
||||
|
||||
function getKeyValue(object, keyPath) {
|
||||
keyPath = keyPath.split('.');
|
||||
for (var i = 0; i < keyPath.length; i++) {
|
||||
if (object && _.has(object, keyPath[i])) {
|
||||
object = object[keyPath[i]];
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return object;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* A global Blaze UI helper to get the thumbnails for the given study
|
||||
*/
|
||||
Template.registerHelper('studyThumbnails', study => {
|
||||
if (!study) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the study's stacks
|
||||
const stacks = study.displaySets;
|
||||
|
||||
// Defines the resulting thumbnails list
|
||||
const thumbnails = [];
|
||||
|
||||
// Iterate over the stacks and add one by one with its index
|
||||
_.each(stacks, (stack, index) => {
|
||||
thumbnails.push({
|
||||
thumbnailIndex: index,
|
||||
stack: stack
|
||||
});
|
||||
});
|
||||
|
||||
return thumbnails;
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
var instanceClassViewportSettingsFunctions = {};
|
||||
getInstanceClassDefaultViewport = function(series, enabledElement, imageId) {
|
||||
var instanceClass = series.sopClassUid;
|
||||
|
||||
if (!instanceClassViewportSettingsFunctions[instanceClass]) {
|
||||
return;
|
||||
}
|
||||
|
||||
return instanceClassViewportSettingsFunctions[instanceClass](series, enabledElement, imageId);
|
||||
};
|
||||
|
||||
setInstanceClassDefaultViewportFunction = function(instanceClass, fn) {
|
||||
instanceClassViewportSettingsFunctions[instanceClass] = fn;
|
||||
};
|
||||
@@ -1,59 +0,0 @@
|
||||
/**
|
||||
* Checks whether dicom files with specified SOP Class UID have image data
|
||||
* @param {string} sopClassUid - SOP Class UID to be checked
|
||||
* @returns {boolean} - true if it has image data
|
||||
*/
|
||||
isImage = function(sopClassUid) {
|
||||
if (sopClassUid == sopClassDictionary.ComputedRadiographyImageStorage
|
||||
|| sopClassUid == sopClassDictionary.DigitalXRayImageStorageForPresentation
|
||||
|| sopClassUid == sopClassDictionary.DigitalXRayImageStorageForProcessing
|
||||
|| sopClassUid == sopClassDictionary.DigitalMammographyXRayImageStorageForPresentation
|
||||
|| sopClassUid == sopClassDictionary.DigitalMammographyXRayImageStorageForProcessing
|
||||
|| sopClassUid == sopClassDictionary.DigitalIntraOralXRayImageStorageForPresentation
|
||||
|| sopClassUid == sopClassDictionary.DigitalIntraOralXRayImageStorageForProcessing
|
||||
|| sopClassUid == sopClassDictionary.CTImageStorage
|
||||
|| sopClassUid == sopClassDictionary.EnhancedCTImageStorage
|
||||
|| sopClassUid == sopClassDictionary.LegacyConvertedEnhancedCTImageStorage
|
||||
|| sopClassUid == sopClassDictionary.UltrasoundMultiframeImageStorage
|
||||
|| sopClassUid == sopClassDictionary.MRImageStorage
|
||||
|| sopClassUid == sopClassDictionary.EnhancedMRImageStorage
|
||||
|| sopClassUid == sopClassDictionary.EnhancedMRColorImageStorage
|
||||
|| sopClassUid == sopClassDictionary.LegacyConvertedEnhancedMRImageStorage
|
||||
|| sopClassUid == sopClassDictionary.UltrasoundImageStorage
|
||||
|| sopClassUid == sopClassDictionary.SecondaryCaptureImageStorage
|
||||
|| sopClassUid == sopClassDictionary.MultiframeSingleBitSecondaryCaptureImageStorage
|
||||
|| sopClassUid == sopClassDictionary.MultiframeGrayscaleByteSecondaryCaptureImageStorage
|
||||
|| sopClassUid == sopClassDictionary.MultiframeGrayscaleWordSecondaryCaptureImageStorage
|
||||
|| sopClassUid == sopClassDictionary.MultiframeTrueColorSecondaryCaptureImageStorage
|
||||
|| sopClassUid == sopClassDictionary.XRayAngiographicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.EnhancedXAImageStorage
|
||||
|| sopClassUid == sopClassDictionary.XRayRadiofluoroscopicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.EnhancedXRFImageStorage
|
||||
|| sopClassUid == sopClassDictionary.XRay3DAngiographicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.XRay3DCraniofacialImageStorage
|
||||
|| sopClassUid == sopClassDictionary.BreastTomosynthesisImageStorage
|
||||
|| sopClassUid == sopClassDictionary.BreastProjectionXRayImageStorageForPresentation
|
||||
|| sopClassUid == sopClassDictionary.BreastProjectionXRayImageStorageForProcessing
|
||||
|| sopClassUid == sopClassDictionary.IntravascularOpticalCoherenceTomographyImageStorageForPresentation
|
||||
|| sopClassUid == sopClassDictionary.IntravascularOpticalCoherenceTomographyImageStorageForProcessing
|
||||
|| sopClassUid == sopClassDictionary.NuclearMedicineImageStorage
|
||||
|| sopClassUid == sopClassDictionary.VLEndoscopicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.VideoEndoscopicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.VLMicroscopicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.VideoMicroscopicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.VLSlideCoordinatesMicroscopicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.VLPhotographicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.VideoPhotographicImageStorage
|
||||
|| sopClassUid == sopClassDictionary.OphthalmicPhotography8BitImageStorage
|
||||
|| sopClassUid == sopClassDictionary.OphthalmicPhotography16BitImageStorage
|
||||
|| sopClassUid == sopClassDictionary.OphthalmicTomographyImageStorage
|
||||
|| sopClassUid == sopClassDictionary.VLWholeSlideMicroscopyImageStorage
|
||||
|| sopClassUid == sopClassDictionary.PositronEmissionTomographyImageStorage
|
||||
|| sopClassUid == sopClassDictionary.EnhancedPETImageStorage
|
||||
|| sopClassUid == sopClassDictionary.LegacyConvertedEnhancedPETImageStorage
|
||||
|| sopClassUid == sopClassDictionary.RTImageStorage) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
@@ -1,234 +0,0 @@
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
// Displays Series in Viewports given a Protocol and list of Studies
|
||||
LayoutManager = class LayoutManager {
|
||||
constructor(parentNode, studies) {
|
||||
log.info('LayoutManager');
|
||||
|
||||
this.parentNode = parentNode;
|
||||
this.studies = studies;
|
||||
this.viewportData = [];
|
||||
this.layoutTemplateName = 'gridLayout';
|
||||
this.layoutProps = {
|
||||
rows: 1,
|
||||
columns: 1
|
||||
};
|
||||
|
||||
this.viewportData = [];
|
||||
|
||||
this.isZoomed = false;
|
||||
|
||||
const updateSessionFn = () => Tracker.afterFlush(() => Session.set('LayoutManagerUpdated', Random.id()));
|
||||
this.updateSession = _.throttle(updateSessionFn, 300);
|
||||
}
|
||||
|
||||
getNumberOfViewports() {
|
||||
return this.layoutProps.rows * this.layoutProps.columns;
|
||||
}
|
||||
|
||||
setDefaultViewportData() {
|
||||
const self = this;
|
||||
|
||||
// Get the number of vieports to be rendered
|
||||
const viewportsAmount = this.getNumberOfViewports();
|
||||
|
||||
// Store the old viewport data and reset the current
|
||||
const oldViewportData = self.viewportData;
|
||||
|
||||
// Get the studies and display sets sequence map
|
||||
const sequenceMap = OHIF.viewer.getDisplaySetSequenceMap();
|
||||
|
||||
// Check if the display sets are sequenced
|
||||
const isSequenced = OHIF.viewer.isDisplaySetsSequenced(sequenceMap);
|
||||
|
||||
// Define the current viewport index and the viewport data array
|
||||
let currentViewportIndex = 0;
|
||||
if (viewportsAmount > oldViewportData.length && oldViewportData.length && isSequenced) {
|
||||
// Keep the displayed display sets
|
||||
self.viewportData = oldViewportData;
|
||||
currentViewportIndex = oldViewportData.length - 1;
|
||||
} else if (viewportsAmount <= oldViewportData.length) {
|
||||
// Reduce the original displayed display sets
|
||||
self.viewportData = oldViewportData.slice(0, viewportsAmount);
|
||||
return;
|
||||
} else {
|
||||
// Reset all display sets
|
||||
self.viewportData = [];
|
||||
}
|
||||
|
||||
// Get all the display sets for the viewer studies
|
||||
let displaySets = [];
|
||||
this.studies.forEach(study => {
|
||||
study.displaySets.forEach(displaySet => {
|
||||
displaySet.images.length && displaySets.push(displaySet);
|
||||
});
|
||||
});
|
||||
|
||||
// Get the display sets that will be appended to the current ones
|
||||
let appendix;
|
||||
const currentLength = self.viewportData.length;
|
||||
if (currentLength) {
|
||||
// TODO: isolate displaySets array by study (maybe a map?)
|
||||
const beginIndex = sequenceMap.values().next().value[0].displaySetIndex + currentLength;
|
||||
const endIndex = beginIndex + (viewportsAmount - currentLength);
|
||||
appendix = displaySets.slice(beginIndex, endIndex);
|
||||
} else {
|
||||
// Get available display sets from the first to the grid size
|
||||
appendix = displaySets.slice(0, viewportsAmount);
|
||||
}
|
||||
|
||||
// Generate the additional data based on the appendix
|
||||
const additionalData = [];
|
||||
appendix.forEach((displaySet, index) => {
|
||||
additionalData.push({
|
||||
viewportIndex: currentViewportIndex + index,
|
||||
studyInstanceUid: displaySet.studyInstanceUid,
|
||||
seriesInstanceUid: displaySet.seriesInstanceUid,
|
||||
displaySetInstanceUid: displaySet.displaySetInstanceUid,
|
||||
sopInstanceUid: displaySet.images[0].sopInstanceUid
|
||||
});
|
||||
});
|
||||
|
||||
// Append the additional data with the viewport data
|
||||
self.viewportData = self.viewportData.concat(additionalData);
|
||||
|
||||
// Push empty objects if the amount is lesser than the grid size
|
||||
while (self.viewportData.length < viewportsAmount) {
|
||||
self.viewportData.push({});
|
||||
}
|
||||
}
|
||||
|
||||
updateViewports() {
|
||||
log.info('updateViewports');
|
||||
|
||||
if (!this.viewportData ||
|
||||
!this.viewportData.length ||
|
||||
this.viewportData.length !== this.getNumberOfViewports()) {
|
||||
this.setDefaultViewportData();
|
||||
}
|
||||
|
||||
// imageViewerViewports occasionally needs relevant layout data in order to set
|
||||
// the element style of the viewport in question
|
||||
var layoutProps = this.layoutProps;
|
||||
var data = $.extend({
|
||||
viewportData: []
|
||||
}, layoutProps);
|
||||
|
||||
this.viewportData.forEach(function(viewportData) {
|
||||
var viewportDataAndLayoutProps = $.extend(viewportData, layoutProps);
|
||||
data.viewportData.push(viewportDataAndLayoutProps);
|
||||
});
|
||||
|
||||
var layoutTemplate = Template[this.layoutTemplateName];
|
||||
|
||||
$(this.parentNode).html('');
|
||||
Blaze.renderWithData(layoutTemplate, data, this.parentNode);
|
||||
|
||||
this.updateSession();
|
||||
|
||||
this.isZoomed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function destroys and re-renders the imageViewerViewport template.
|
||||
* It uses the data provided to load a new display set into the produced viewport.
|
||||
*
|
||||
* @param viewportIndex
|
||||
* @param data
|
||||
*/
|
||||
rerenderViewportWithNewDisplaySet(viewportIndex, data) {
|
||||
// The parent container is identified because it is later removed from the DOM
|
||||
var element = $('.imageViewerViewport').eq(viewportIndex);
|
||||
var container = element.parents('.viewportContainer').get(0);
|
||||
|
||||
// Record the current viewportIndex so this can be passed into the re-rendering call
|
||||
data.viewportIndex = viewportIndex;
|
||||
|
||||
// Update the dictionary of loaded displaySet for the specified viewport
|
||||
this.viewportData[viewportIndex] = {
|
||||
viewportIndex: viewportIndex,
|
||||
displaySetInstanceUid: data.displaySetInstanceUid,
|
||||
seriesInstanceUid: data.seriesInstanceUid,
|
||||
studyInstanceUid: data.studyInstanceUid,
|
||||
renderedCallback: data.renderedCallback,
|
||||
currentImageIdIndex: 0
|
||||
};
|
||||
|
||||
// Remove the hover styling
|
||||
element.find('canvas').not('.magnifyTool').removeClass('faded');
|
||||
|
||||
// Remove the whole template, add in the new one
|
||||
var viewportContainer = element.parents('.removable');
|
||||
|
||||
var newViewportContainer = document.createElement('div');
|
||||
newViewportContainer.className = 'removable';
|
||||
|
||||
// Remove the parent element of the template
|
||||
// This is a workaround since otherwise Blaze UI onDestroyed doesn't fire
|
||||
viewportContainer.remove();
|
||||
|
||||
container.appendChild(newViewportContainer);
|
||||
|
||||
// Render and insert the template
|
||||
Blaze.renderWithData(Template.imageViewerViewport, data, newViewportContainer);
|
||||
|
||||
this.updateSession();
|
||||
}
|
||||
|
||||
enlargeViewport(viewportIndex) {
|
||||
log.info('Zooming Into Viewport: ' + viewportIndex);
|
||||
|
||||
if (!this.viewportData ||
|
||||
!this.viewportData.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clone the array for later
|
||||
this.previousViewportData = this.viewportData.slice(0);
|
||||
|
||||
var singleViewportData = $.extend({}, this.viewportData[viewportIndex]);
|
||||
singleViewportData.rows = 1;
|
||||
singleViewportData.columns = 1;
|
||||
singleViewportData.viewportIndex = 0;
|
||||
|
||||
var data = {
|
||||
viewportData: [singleViewportData],
|
||||
rows: 1,
|
||||
columns: 1
|
||||
};
|
||||
|
||||
var layoutTemplate = Template['gridLayout'];
|
||||
|
||||
$(this.parentNode).html('');
|
||||
Blaze.renderWithData(layoutTemplate, data, this.parentNode);
|
||||
|
||||
this.isZoomed = true;
|
||||
this.zoomedViewportIndex = viewportIndex;
|
||||
this.viewportData = data.viewportData;
|
||||
|
||||
this.updateSession();
|
||||
}
|
||||
|
||||
resetPreviousLayout() {
|
||||
if (!this.isZoomed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.previousViewportData[this.zoomedViewportIndex] = $.extend({}, this.viewportData[0]);
|
||||
this.previousViewportData[this.zoomedViewportIndex].viewportIndex = this.zoomedViewportIndex;
|
||||
this.viewportData = this.previousViewportData;
|
||||
this.updateViewports();
|
||||
}
|
||||
|
||||
toggleEnlargement(viewportIndex) {
|
||||
if (this.isZoomed) {
|
||||
this.resetPreviousLayout();
|
||||
} else {
|
||||
// Don't enlarge the viewport if we only have one Viewport
|
||||
// to begin with
|
||||
if (this.getNumberOfViewports() > 1) {
|
||||
this.enlargeViewport(viewportIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,244 +0,0 @@
|
||||
import {parsingUtils} from './parsingUtils'
|
||||
|
||||
var metaDataLookup = {};
|
||||
|
||||
/**
|
||||
* Cornerstone MetaData provider to store image meta data
|
||||
* Data from instances, series, and studies are associated with
|
||||
* imageIds to facilitate usage of this information by Cornerstone's Tools
|
||||
*
|
||||
* e.g. the imagePlane metaData object contains instance information about
|
||||
* row/column pixel spacing, patient position, and patient orientation. It
|
||||
* is used in CornerstoneTools to position reference lines and orientation markers.
|
||||
*
|
||||
* @param {String} imageId The Cornerstone ImageId
|
||||
* @param {Object} data An object containing instance, series, and study metaData
|
||||
*/
|
||||
addMetaData = function(imageId, data) {
|
||||
var instanceMetaData = data.instance;
|
||||
var seriesMetaData = data.series;
|
||||
var studyMetaData = data.study;
|
||||
var imageIndex = data.imageIndex;
|
||||
var numImages = data.numImages;
|
||||
|
||||
var metaData = {};
|
||||
|
||||
metaData.study = {
|
||||
patientId: studyMetaData.patientId,
|
||||
studyInstanceUid: studyMetaData.studyInstanceUid,
|
||||
studyDate: studyMetaData.studyDate,
|
||||
studyTime: studyMetaData.studyTime,
|
||||
studyDescription: studyMetaData.studyDescription,
|
||||
institutionName: studyMetaData.institutionName
|
||||
};
|
||||
|
||||
metaData.series = {
|
||||
seriesDescription: seriesMetaData.seriesDescription,
|
||||
seriesNumber: seriesMetaData.seriesNumber,
|
||||
modality: seriesMetaData.modality,
|
||||
seriesInstanceUid: seriesMetaData.seriesInstanceUid,
|
||||
numImages: numImages
|
||||
};
|
||||
|
||||
metaData.instance = instanceMetaData;
|
||||
|
||||
metaData.patient = {
|
||||
name: studyMetaData.patientName,
|
||||
id: studyMetaData.patientId,
|
||||
birthDate: studyMetaData.patientBirthDate,
|
||||
sex: studyMetaData.patientSex
|
||||
};
|
||||
|
||||
// If there is sufficient information, populate
|
||||
// the imagePlane object for easier use in the Viewer
|
||||
metaData.imagePlane = getImagePlane(instanceMetaData);
|
||||
|
||||
// Add the metaData to the imageId lookup object
|
||||
metaDataLookup[imageId] = metaData;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a set of metaData to the Cornerstone metaData provider given a specific
|
||||
* imageId, type, and dataset
|
||||
*
|
||||
* @param imageId
|
||||
* @param type (e.g. series, instance, tagDisplay)
|
||||
* @param data
|
||||
*/
|
||||
addSpecificMetadata = function(imageId, type, data) {
|
||||
var metaData = {};
|
||||
metaData[type] = data;
|
||||
|
||||
metaDataLookup[imageId] = $.extend(metaDataLookup[imageId], metaData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the related metaData for missing fields given a specified image
|
||||
*
|
||||
* @param image
|
||||
*/
|
||||
updateMetaData = function(image) {
|
||||
var imageMetaData = metaDataLookup[image.imageId];
|
||||
if (!imageMetaData) {
|
||||
return;
|
||||
}
|
||||
|
||||
imageMetaData.instance.sopClassUid = imageMetaData.instance.sopClassUid || image.data.string('x00080016');
|
||||
imageMetaData.instance.sopInstanceUid = imageMetaData.instance.sopInstanceUid || image.data.string('x00080018');
|
||||
|
||||
imageMetaData.instance.rows = imageMetaData.instance.rows || image.rows;
|
||||
imageMetaData.instance.columns = imageMetaData.instance.columns || image.columns;
|
||||
imageMetaData.instance.pixelSpacing = imageMetaData.instance.pixelSpacing || image.data.string('x00280030');
|
||||
imageMetaData.instance.frameOfReferenceUID = imageMetaData.instance.frameOfReferenceUID || image.data.string('x00200052');
|
||||
imageMetaData.instance.imageOrientationPatient = imageMetaData.instance.imageOrientationPatient || image.data.string('x00200037');
|
||||
imageMetaData.instance.imagePositionPatient = imageMetaData.instance.imagePositionPatient || image.data.string('x00200032');
|
||||
|
||||
imageMetaData.instance.sliceThickness = imageMetaData.instance.sliceThickness || image.data.string('x00180050');
|
||||
imageMetaData.instance.sliceLocation = imageMetaData.instance.sliceLocation || image.data.string('x00201041');
|
||||
imageMetaData.instance.tablePosition = imageMetaData.instance.tablePosition || image.data.string('x00189327');
|
||||
imageMetaData.instance.spacingBetweenSlices = imageMetaData.instance.spacingBetweenSlices || image.data.string('x00180088');
|
||||
|
||||
imageMetaData.instance.lossyImageCompression = imageMetaData.instance.lossyImageCompression || image.data.string('x00282110');
|
||||
imageMetaData.instance.lossyImageCompressionRatio = imageMetaData.instance.lossyImageCompressionRatio || image.data.string('x00282112');
|
||||
|
||||
imageMetaData.instance.frameIncrementPointer = imageMetaData.instance.frameIncrementPointer || image.data.string('x00280009');
|
||||
imageMetaData.instance.frameTime = imageMetaData.instance.frameTime || image.data.string('x00181063');
|
||||
imageMetaData.instance.frameTimeVector = imageMetaData.instance.frameTimeVector || image.data.string('x00181065');
|
||||
|
||||
if (!imageMetaData.instance.multiframeMetadata) {
|
||||
imageMetaData.instance.multiframeMetadata = getMultiframeModuleMetaData(image.data);
|
||||
}
|
||||
|
||||
imageMetaData.imagePlane = imageMetaData.imagePlane || getImagePlane(imageMetaData.instance);
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs and returns the imagePlane given the instance
|
||||
*
|
||||
* @param instance The instance containing information to construct imagePlane
|
||||
* @returns imagePlane The constructed imagePlane to be used in viewer easily
|
||||
*/
|
||||
function getImagePlane(instance) {
|
||||
if (!instance.rows || !instance.columns || !instance.pixelSpacing ||
|
||||
!instance.frameOfReferenceUID || !instance.imageOrientationPatient ||
|
||||
!instance.imagePositionPatient) {
|
||||
return;
|
||||
}
|
||||
|
||||
var imageOrientation = instance.imageOrientationPatient.split('\\');
|
||||
var imagePosition = instance.imagePositionPatient.split('\\');
|
||||
|
||||
var columnPixelSpacing = 1.0;
|
||||
var rowPixelSpacing = 1.0;
|
||||
if (instance.pixelSpacing) {
|
||||
var split = instance.pixelSpacing.split('\\');
|
||||
rowPixelSpacing = parseFloat(split[0]);
|
||||
columnPixelSpacing = parseFloat(split[1]);
|
||||
}
|
||||
|
||||
return {
|
||||
frameOfReferenceUID:
|
||||
instance.frameOfReferenceUID,
|
||||
rows:
|
||||
instance.rows,
|
||||
columns:
|
||||
instance.columns,
|
||||
rowCosines:
|
||||
new cornerstoneMath.Vector3(parseFloat(imageOrientation[0]), parseFloat(imageOrientation[1]), parseFloat(imageOrientation[2])),
|
||||
columnCosines:
|
||||
new cornerstoneMath.Vector3(parseFloat(imageOrientation[3]), parseFloat(imageOrientation[4]), parseFloat(imageOrientation[5])),
|
||||
imagePositionPatient:
|
||||
new cornerstoneMath.Vector3(parseFloat(imagePosition[0]), parseFloat(imagePosition[1]), parseFloat(imagePosition[2])),
|
||||
rowPixelSpacing:
|
||||
rowPixelSpacing,
|
||||
columnPixelSpacing:
|
||||
columnPixelSpacing,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This function extracts miltiframe information from a dicomParser.DataSet object.
|
||||
*
|
||||
* @param dataSet {Object} An instance of dicomParser.DataSet object where multiframe information can be found.
|
||||
* @return {Object} An object containing multiframe image metadata (frameIncrementPointer, frameTime, frameTimeVector, etc).
|
||||
*/
|
||||
getMultiframeModuleMetaData = function(dataSet) {
|
||||
|
||||
var numberOfFrames,
|
||||
frameIncrementPointer,
|
||||
frameTime,
|
||||
frameTimeVector,
|
||||
imageInfo = {
|
||||
isMultiframeImage: false,
|
||||
frameIncrementPointer: null,
|
||||
numberOfFrames: 0,
|
||||
frameTime: 0,
|
||||
frameTimeVector: null,
|
||||
averageFrameRate: 0 // backwards compatibility only... it might be useless in the future
|
||||
};
|
||||
|
||||
if (parsingUtils.isValidDataSet(dataSet)) {
|
||||
|
||||
// (0028,0008) = Number of Frames
|
||||
numberOfFrames = dataSet.intString('x00280008', -1);
|
||||
if (numberOfFrames > 0) {
|
||||
|
||||
// set multi-frame image indicator
|
||||
imageInfo.isMultiframeImage = true;
|
||||
imageInfo.numberOfFrames = numberOfFrames;
|
||||
|
||||
// (0028,0009) = Frame Increment Pointer
|
||||
frameIncrementPointer = parsingUtils.attributeTag(dataSet, 'x00280009') || '';
|
||||
|
||||
if (frameIncrementPointer === 'x00181065') {
|
||||
// Frame Increment Pointer points to Frame Time Vector (0018,1065) field
|
||||
frameTimeVector = parsingUtils.floatArray(dataSet, 'x00181065');
|
||||
if (frameTimeVector instanceof Array && frameTimeVector.length > 0) {
|
||||
imageInfo.frameIncrementPointer = 'frameTimeVector';
|
||||
imageInfo.frameTimeVector = frameTimeVector;
|
||||
frameTime = frameTimeVector.reduce((a, b) => a + b) / frameTimeVector.length;
|
||||
imageInfo.averageFrameRate = 1000 / frameTime;
|
||||
}
|
||||
} else if (frameIncrementPointer === 'x00181063' || frameIncrementPointer === '') {
|
||||
// Frame Increment Pointer points to Frame Time (0018,1063) field or is not defined (for addtional flexibility).
|
||||
// Yet another value is possible for this field (5200,9230 for Multi-frame Functional Groups)
|
||||
// but that case is currently not supported.
|
||||
frameTime = dataSet.floatString('x00181063', -1);
|
||||
if (frameTime > 0) {
|
||||
imageInfo.frameIncrementPointer = 'frameTime';
|
||||
imageInfo.frameTime = frameTime;
|
||||
imageInfo.averageFrameRate = 1000 / frameTime;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return imageInfo;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Looks up metaData for Cornerstone Tools given a specified type and imageId
|
||||
* A type may be, e.g. 'study', or 'patient', or 'imagePlane'. These types
|
||||
* are keys in the stored metaData objects.
|
||||
*
|
||||
* @param type
|
||||
* @param imageId
|
||||
* @returns {Object} Relevant metaData of the specified type
|
||||
*/
|
||||
function provider(type, imageId) {
|
||||
var imageMetaData = metaDataLookup[imageId];
|
||||
if (!imageMetaData) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (imageMetaData.hasOwnProperty(type)) {
|
||||
return imageMetaData[type];
|
||||
}
|
||||
}
|
||||
|
||||
Meteor.startup(function() {
|
||||
cornerstoneTools.metaData.addProvider(provider);
|
||||
});
|
||||
@@ -1,49 +0,0 @@
|
||||
var resizeTimer;
|
||||
|
||||
handleResize = function() {
|
||||
// Avoid doing DOM manipulation during the resize handler
|
||||
// because it is fired very often.
|
||||
// Resizing is therefore performed 100 ms after the resize event stops.
|
||||
clearTimeout(resizeTimer);
|
||||
resizeTimer = setTimeout(function() {
|
||||
resizeViewportElements();
|
||||
}, 100);
|
||||
};
|
||||
|
||||
// Resize viewport elements
|
||||
resizeViewportElements = function() {
|
||||
viewportResizeTimer = setTimeout(function() {
|
||||
var elements = $('.imageViewerViewport').not('.empty');
|
||||
elements.each(function(index, element) {
|
||||
var enabledElement;
|
||||
try {
|
||||
enabledElement = cornerstone.getEnabledElement(element);
|
||||
} catch(error) {
|
||||
return;
|
||||
}
|
||||
|
||||
cornerstone.resize(element, true);
|
||||
|
||||
if (enabledElement.fitToWindow === false) {
|
||||
var imageId = enabledElement.image.imageId;
|
||||
var instance = cornerstoneTools.metaData.get('instance', imageId);
|
||||
var instanceClassViewport = getInstanceClassDefaultViewport(instance, enabledElement, imageId);
|
||||
cornerstone.setViewport(element, instanceClassViewport);
|
||||
}
|
||||
|
||||
// TODO= Refactor this into separate scrollbar resize function
|
||||
var currentOverlay = $(element).siblings('.imageViewerViewportOverlay');
|
||||
var imageControls = currentOverlay.find('.imageControls');
|
||||
currentOverlay.find('.imageControls').height($(element).height());
|
||||
|
||||
// Set it's width to its parent's height
|
||||
// (because webkit is stupid and can't style vertical sliders)
|
||||
var scrollbar = currentOverlay.find('#scrollbar');
|
||||
scrollbar.height(scrollbar.parent().height() - 20);
|
||||
|
||||
var currentImageSlider = currentOverlay.find('#imageSlider');
|
||||
var overlayHeight = currentImageSlider.parent().height();
|
||||
currentImageSlider.width(overlayHeight);
|
||||
});
|
||||
}, 1);
|
||||
};
|
||||
@@ -1,35 +0,0 @@
|
||||
setActiveViewport = element => {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewportIndex = $('.imageViewerViewport').index(element);
|
||||
const jQueryElement = $(element);
|
||||
|
||||
// When an ActivateViewport event is fired, update the Meteor Session
|
||||
// with the viewport index that it was fired from.
|
||||
Session.set('activeViewport', viewportIndex);
|
||||
|
||||
// Update the Session variable to the UI re-renders
|
||||
Session.set('LayoutManagerUpdated', Random.id());
|
||||
|
||||
// Add the 'active' class to the parent container to highlight the active viewport
|
||||
$('#imageViewerViewports .viewportContainer').removeClass('active');
|
||||
jQueryElement.parents('.viewportContainer').addClass('active');
|
||||
|
||||
// Finally, enable stack prefetching and hide the reference lines from
|
||||
// the newly activated viewport that has a canvas
|
||||
|
||||
if (jQueryElement.find('canvas').length) {
|
||||
// Cornerstone Tools compare DOM elements (check getEnabledElement cornerstone function)
|
||||
// so we can't pass a jQuery object as an argument, otherwise it throws an excepetion
|
||||
const domElement = jQueryElement.get(0);
|
||||
enablePrefetchOnElement(domElement);
|
||||
displayReferenceLines(domElement);
|
||||
}
|
||||
|
||||
// Set the div to focused, so keypress events are handled
|
||||
//$(element).focus();
|
||||
//.focus() event breaks in FF&IE
|
||||
jQueryElement.triggerHandler('focus');
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* Restores the browser focus to the currently specified active viewport
|
||||
* as determined from Meteor's Session variable.
|
||||
*
|
||||
* This is allows keydown events to be captured on the focused element
|
||||
*/
|
||||
setFocusToActiveViewport = function() {
|
||||
// Get the list of viewports
|
||||
var viewports = $('.imageViewerViewport');
|
||||
|
||||
// Get the current active viewport index from Session
|
||||
var activeViewportIndex = Session.get('activeViewport');
|
||||
|
||||
// Find the div from the list of viewports
|
||||
var activeViewport = viewports.eq(activeViewportIndex);
|
||||
|
||||
// Set the browser focus to this div
|
||||
activeViewport.focus();
|
||||
};
|
||||
@@ -1,61 +0,0 @@
|
||||
setMammogramViewportAlignment = function(series, enabledElement, imageId) {
|
||||
// Don't apply the MG viewport alignment to other series types
|
||||
var viewTypes = ['MLO', 'CC', 'LM', 'ML', 'XCCL'];
|
||||
var requiresInversion = ['LM']; // Tomo series are flipped
|
||||
|
||||
var instance = cornerstoneTools.metaData.get('instance', imageId);
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
|
||||
var laterality = instance.laterality;
|
||||
var element = enabledElement.element;
|
||||
var position;
|
||||
|
||||
var left = $(enabledElement.canvas).offset().left;
|
||||
var right = left + enabledElement.canvas.width;
|
||||
|
||||
if (viewTypes.indexOf(instance.viewPosition) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we should flip the laterality
|
||||
if (requiresInversion.indexOf(instance.viewPosition) > -1) {
|
||||
if (laterality === "R") {
|
||||
laterality = "L";
|
||||
} else if (laterality === "L") {
|
||||
laterality = "R";
|
||||
}
|
||||
}
|
||||
|
||||
if (laterality === "R") {
|
||||
// Set X translation to Canvas max in image pixels - image width
|
||||
// This places it on the right side of the screen
|
||||
position = cornerstone.pageToPixel(element, right, 0);
|
||||
if (position.x !== enabledElement.image.width) {
|
||||
enabledElement.viewport.translation.x += position.x - enabledElement.image.width;
|
||||
}
|
||||
|
||||
addSpecificMetadata(imageId, 'tagDisplay', {
|
||||
side: 'L'
|
||||
});
|
||||
|
||||
} else if (laterality === 'L') {
|
||||
// Use pageToPixel to and jQuery to find pixel coordinates of leftmost
|
||||
// side of the current canvas
|
||||
position = cornerstone.pageToPixel(element, left, 0);
|
||||
if (position.x !== 0) {
|
||||
enabledElement.viewport.translation.x += position.x;
|
||||
}
|
||||
|
||||
addSpecificMetadata(imageId, 'tagDisplay', {
|
||||
side: 'R'
|
||||
});
|
||||
}
|
||||
|
||||
return enabledElement.viewport;
|
||||
};
|
||||
|
||||
Meteor.startup(function() {
|
||||
setInstanceClassDefaultViewportFunction("1.2.840.10008.5.1.4.1.1.1.2", setMammogramViewportAlignment);
|
||||
});
|
||||
@@ -1,115 +0,0 @@
|
||||
sopClassDictionary = {
|
||||
ComputedRadiographyImageStorage: "1.2.840.10008.5.1.4.1.1.1",
|
||||
DigitalXRayImageStorageForPresentation: "1.2.840.10008.5.1.4.1.1.1.1",
|
||||
DigitalXRayImageStorageForProcessing: "1.2.840.10008.5.1.4.1.1.1.1.1",
|
||||
DigitalMammographyXRayImageStorageForPresentation: "1.2.840.10008.5.1.4.1.1.1.2",
|
||||
DigitalMammographyXRayImageStorageForProcessing: "1.2.840.10008.5.1.4.1.1.1.2.1",
|
||||
DigitalIntraOralXRayImageStorageForPresentation: "1.2.840.10008.5.1.4.1.1.1.3",
|
||||
DigitalIntraOralXRayImageStorageForProcessing: "1.2.840.10008.5.1.4.1.1.1.3.1",
|
||||
CTImageStorage: "1.2.840.10008.5.1.4.1.1.2",
|
||||
EnhancedCTImageStorage: "1.2.840.10008.5.1.4.1.1.2.1",
|
||||
LegacyConvertedEnhancedCTImageStorage: "1.2.840.10008.5.1.4.1.1.2.2",
|
||||
UltrasoundMultiframeImageStorage: "1.2.840.10008.5.1.4.1.1.3.1",
|
||||
MRImageStorage: "1.2.840.10008.5.1.4.1.1.4",
|
||||
EnhancedMRImageStorage: "1.2.840.10008.5.1.4.1.1.4.1",
|
||||
MRSpectroscopyStorage: "1.2.840.10008.5.1.4.1.1.4.2",
|
||||
EnhancedMRColorImageStorage: "1.2.840.10008.5.1.4.1.1.4.3",
|
||||
LegacyConvertedEnhancedMRImageStorage: "1.2.840.10008.5.1.4.1.1.4.4",
|
||||
UltrasoundImageStorage: "1.2.840.10008.5.1.4.1.1.6.1",
|
||||
EnhancedUSVolumeStorage: "1.2.840.10008.5.1.4.1.1.6.2",
|
||||
SecondaryCaptureImageStorage: "1.2.840.10008.5.1.4.1.1.7",
|
||||
MultiframeSingleBitSecondaryCaptureImageStorage: "1.2.840.10008.5.1.4.1.1.7.1",
|
||||
MultiframeGrayscaleByteSecondaryCaptureImageStorage: "1.2.840.10008.5.1.4.1.1.7.2",
|
||||
MultiframeGrayscaleWordSecondaryCaptureImageStorage: "1.2.840.10008.5.1.4.1.1.7.3",
|
||||
MultiframeTrueColorSecondaryCaptureImageStorage: "1.2.840.10008.5.1.4.1.1.7.4",
|
||||
Sop12LeadECGWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.1.1",
|
||||
GeneralECGWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.1.2",
|
||||
AmbulatoryECGWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.1.3",
|
||||
HemodynamicWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.2.1",
|
||||
CardiacElectrophysiologyWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.3.1",
|
||||
BasicVoiceAudioWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.4.1",
|
||||
GeneralAudioWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.4.2",
|
||||
ArterialPulseWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.5.1",
|
||||
RespiratoryWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.6.1",
|
||||
GrayscaleSoftcopyPresentationStateStorage: "1.2.840.10008.5.1.4.1.1.11.1",
|
||||
ColorSoftcopyPresentationStateStorage: "1.2.840.10008.5.1.4.1.1.11.2",
|
||||
PseudoColorSoftcopyPresentationStateStorage: "1.2.840.10008.5.1.4.1.1.11.3",
|
||||
BlendingSoftcopyPresentationStateStorage: "1.2.840.10008.5.1.4.1.1.11.4",
|
||||
XAXRFGrayscaleSoftcopyPresentationStateStorage: "1.2.840.10008.5.1.4.1.1.11.5",
|
||||
XRayAngiographicImageStorage: "1.2.840.10008.5.1.4.1.1.12.1",
|
||||
EnhancedXAImageStorage: "1.2.840.10008.5.1.4.1.1.12.1.1",
|
||||
XRayRadiofluoroscopicImageStorage: "1.2.840.10008.5.1.4.1.1.12.2",
|
||||
EnhancedXRFImageStorage: "1.2.840.10008.5.1.4.1.1.12.2.1",
|
||||
XRay3DAngiographicImageStorage: "1.2.840.10008.5.1.4.1.1.13.1.1",
|
||||
XRay3DCraniofacialImageStorage: "1.2.840.10008.5.1.4.1.1.13.1.2",
|
||||
BreastTomosynthesisImageStorage: "1.2.840.10008.5.1.4.1.1.13.1.3",
|
||||
BreastProjectionXRayImageStorageForPresentation: "1.2.840.10008.5.1.4.1.1.13.1.4",
|
||||
BreastProjectionXRayImageStorageForProcessing: "1.2.840.10008.5.1.4.1.1.13.1.5",
|
||||
IntravascularOpticalCoherenceTomographyImageStorageForPresentation: "1.2.840.10008.5.1.4.1.1.14.1",
|
||||
IntravascularOpticalCoherenceTomographyImageStorageForProcessing: "1.2.840.10008.5.1.4.1.1.14.2",
|
||||
NuclearMedicineImageStorage: "1.2.840.10008.5.1.4.1.1.20",
|
||||
RawDataStorage: "1.2.840.10008.5.1.4.1.1.66",
|
||||
SpatialRegistrationStorage: "1.2.840.10008.5.1.4.1.1.66.1",
|
||||
SpatialFiducialsStorage: "1.2.840.10008.5.1.4.1.1.66.2",
|
||||
DeformableSpatialRegistrationStorage: "1.2.840.10008.5.1.4.1.1.66.3",
|
||||
SegmentationStorage: "1.2.840.10008.5.1.4.1.1.66.4",
|
||||
SurfaceSegmentationStorage: "1.2.840.10008.5.1.4.1.1.66.5",
|
||||
RealWorldValueMappingStorage: "1.2.840.10008.5.1.4.1.1.67",
|
||||
SurfaceScanMeshStorage: "1.2.840.10008.5.1.4.1.1.68.1",
|
||||
SurfaceScanPointCloudStorage: "1.2.840.10008.5.1.4.1.1.68.2",
|
||||
VLEndoscopicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.1",
|
||||
VideoEndoscopicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.1.1",
|
||||
VLMicroscopicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.2",
|
||||
VideoMicroscopicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.2.1",
|
||||
VLSlideCoordinatesMicroscopicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.3",
|
||||
VLPhotographicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.4",
|
||||
VideoPhotographicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.4.1",
|
||||
OphthalmicPhotography8BitImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.5.1",
|
||||
OphthalmicPhotography16BitImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.5.2",
|
||||
StereometricRelationshipStorage: "1.2.840.10008.5.1.4.1.1.77.1.5.3",
|
||||
OphthalmicTomographyImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.5.4",
|
||||
VLWholeSlideMicroscopyImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.6",
|
||||
LensometryMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.78.1",
|
||||
AutorefractionMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.78.2",
|
||||
KeratometryMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.78.3",
|
||||
SubjectiveRefractionMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.78.4",
|
||||
VisualAcuityMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.78.5",
|
||||
SpectaclePrescriptionReportStorage: "1.2.840.10008.5.1.4.1.1.78.6",
|
||||
OphthalmicAxialMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.78.7",
|
||||
IntraocularLensCalculationsStorage: "1.2.840.10008.5.1.4.1.1.78.8",
|
||||
MacularGridThicknessandVolumeReport: "1.2.840.10008.5.1.4.1.1.79.1",
|
||||
OphthalmicVisualFieldStaticPerimetryMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.80.1",
|
||||
OphthalmicThicknessMapStorage: "1.2.840.10008.5.1.4.1.1.81.1",
|
||||
CornealTopographyMapStorage: "1.2.840.10008.5.1.4.1.1.82.1",
|
||||
BasicTextSR: "1.2.840.10008.5.1.4.1.1.88.11",
|
||||
EnhancedSR: "1.2.840.10008.5.1.4.1.1.88.22",
|
||||
ComprehensiveSR: "1.2.840.10008.5.1.4.1.1.88.33",
|
||||
Comprehensive3DSR: "1.2.840.10008.5.1.4.1.1.88.34",
|
||||
ProcedureLog: "1.2.840.10008.5.1.4.1.1.88.40",
|
||||
MammographyCADSR: "1.2.840.10008.5.1.4.1.1.88.50",
|
||||
KeyObjectSelection: "1.2.840.10008.5.1.4.1.1.88.59",
|
||||
ChestCADSR: "1.2.840.10008.5.1.4.1.1.88.65",
|
||||
XRayRadiationDoseSR: "1.2.840.10008.5.1.4.1.1.88.67",
|
||||
RadiopharmaceuticalRadiationDoseSR: "1.2.840.10008.5.1.4.1.1.88.68",
|
||||
ColonCADSR: "1.2.840.10008.5.1.4.1.1.88.69",
|
||||
ImplantationPlanSRDocumentStorage: "1.2.840.10008.5.1.4.1.1.88.70",
|
||||
EncapsulatedPDFStorage: "1.2.840.10008.5.1.4.1.1.104.1",
|
||||
EncapsulatedCDAStorage: "1.2.840.10008.5.1.4.1.1.104.2",
|
||||
PositronEmissionTomographyImageStorage: "1.2.840.10008.5.1.4.1.1.128",
|
||||
EnhancedPETImageStorage: "1.2.840.10008.5.1.4.1.1.130",
|
||||
LegacyConvertedEnhancedPETImageStorage: "1.2.840.10008.5.1.4.1.1.128.1",
|
||||
BasicStructuredDisplayStorage: "1.2.840.10008.5.1.4.1.1.131",
|
||||
RTImageStorage: "1.2.840.10008.5.1.4.1.1.481.1",
|
||||
RTDoseStorage: "1.2.840.10008.5.1.4.1.1.481.2",
|
||||
RTStructureSetStorage: "1.2.840.10008.5.1.4.1.1.481.3",
|
||||
RTBeamsTreatmentRecordStorage: "1.2.840.10008.5.1.4.1.1.481.4",
|
||||
RTPlanStorage: "1.2.840.10008.5.1.4.1.1.481.5",
|
||||
RTBrachyTreatmentRecordStorage: "1.2.840.10008.5.1.4.1.1.481.6",
|
||||
RTTreatmentSummaryRecordStorage: "1.2.840.10008.5.1.4.1.1.481.7",
|
||||
RTIonPlanStorage: "1.2.840.10008.5.1.4.1.1.481.8",
|
||||
RTIonBeamsTreatmentRecordStorage: "1.2.840.10008.5.1.4.1.1.481.9",
|
||||
RTBeamsDeliveryInstructionStorage: "1.2.840.10008.5.1.4.34.7",
|
||||
GenericImplantTemplateStorage: "1.2.840.10008.5.1.4.43.1",
|
||||
ImplantAssemblyTemplateStorage: "1.2.840.10008.5.1.4.44.1",
|
||||
ImplantTemplateGroupStorage: "1.2.840.10008.5.1.4.45.1"
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Sorts the series and instances inside a study instance by their series
|
||||
* and instance numbers in ascending order.
|
||||
*
|
||||
* @param {Object} study The study instance
|
||||
*/
|
||||
sortStudy = function(study) {
|
||||
if (!study || !study.seriesList) {
|
||||
throw "Insufficient study data was provided to sortStudy";
|
||||
}
|
||||
|
||||
study.seriesList.sort(function(a, b) {
|
||||
return a.seriesNumber - b.seriesNumber;
|
||||
});
|
||||
|
||||
study.seriesList.forEach(function(series){
|
||||
series.instances.sort(function(a, b) {
|
||||
return a.instanceNumber - b.instanceNumber;
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,13 +0,0 @@
|
||||
/**
|
||||
* This function switches to an image given an element and the index of the image in the current stack
|
||||
* Note: Negative indexing is supported:
|
||||
*
|
||||
* e.g. switchToImageByIndex(element, -1) to switch to the last image of the stack
|
||||
*
|
||||
* @param element
|
||||
* @param {number} [newImageIdIndex] The image index in the stack to switch to.
|
||||
*/
|
||||
switchToImageByIndex = function(newImageIdIndex) {
|
||||
var element = getActiveViewportElement();
|
||||
cornerstoneTools.scrollToIndex(element, newImageIdIndex);
|
||||
};
|
||||
@@ -1,13 +0,0 @@
|
||||
/**
|
||||
* This function switches to an image given an element and
|
||||
* the relative distance from the current image in the stack
|
||||
*
|
||||
* e.g. switchToImageRelative(element, -1) to switch to currentImageIdIndex - 1
|
||||
*
|
||||
* @param element
|
||||
* @param {number} [distanceFromCurrentIndex] The image index in the stack to switch to.
|
||||
*/
|
||||
switchToImageRelative = function(distanceFromCurrentIndex) {
|
||||
var element = getActiveViewportElement();
|
||||
cornerstoneTools.scroll(element, distanceFromCurrentIndex);
|
||||
};
|
||||
@@ -1,6 +0,0 @@
|
||||
toggleDialog = element => {
|
||||
const $element = $(element);
|
||||
const isClosed = $element.hasClass('dialog-open');
|
||||
$element.toggleClass('dialog-closed', isClosed);
|
||||
$element.toggleClass('dialog-open', !isClosed);
|
||||
};
|
||||
@@ -1,240 +0,0 @@
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
var activeTool = 'wwwc';
|
||||
var defaultTool = 'wwwc';
|
||||
|
||||
var tools = {};
|
||||
|
||||
var toolDefaultStates = {
|
||||
activate: [],
|
||||
deactivate: ['length', 'angle', 'annotate', 'ellipticalRoi', 'rectangleRoi'],
|
||||
enable: [],
|
||||
disable: [],
|
||||
disabledToolButtons: []
|
||||
};
|
||||
|
||||
var initialized = false;
|
||||
|
||||
function configureTools() {
|
||||
// Set the configuration for the multitouch pan tool
|
||||
var multiTouchPanConfig = {
|
||||
testPointers: function(eventData) {
|
||||
return (eventData.numPointers >= 3);
|
||||
}
|
||||
};
|
||||
cornerstoneTools.panMultiTouch.setConfiguration(multiTouchPanConfig);
|
||||
|
||||
// Set the text box background color
|
||||
cornerstoneTools.textStyle.setBackgroundColor('rgba(0, 0, 0, 0.95)');
|
||||
|
||||
// Set the tool width
|
||||
cornerstoneTools.toolStyle.setToolWidth(2);
|
||||
|
||||
// Set color for inactive tools
|
||||
cornerstoneTools.toolColors.setToolColor('#ff00ff'); //rgb(255, 255, 0)');
|
||||
|
||||
// Set color for active tools
|
||||
cornerstoneTools.toolColors.setActiveColor('#00ffff'); //rgb(0, 255, 0)'
|
||||
|
||||
// Set the configuration values for the text annotation (Arrow) tool
|
||||
const annotateConfig = {
|
||||
getTextCallback: getAnnotationTextCallback,
|
||||
changeTextCallback: changeAnnotationTextCallback,
|
||||
drawHandles: false,
|
||||
arrowFirst: true
|
||||
};
|
||||
cornerstoneTools.arrowAnnotate.setConfiguration(annotateConfig);
|
||||
|
||||
const zoomConfig = {
|
||||
minScale: 0.05,
|
||||
maxScale: 10
|
||||
};
|
||||
cornerstoneTools.zoom.setConfiguration(zoomConfig);
|
||||
}
|
||||
|
||||
toolManager = {
|
||||
init: function() {
|
||||
toolManager.addTool('wwwc', {
|
||||
mouse: cornerstoneTools.wwwc,
|
||||
touch: cornerstoneTools.wwwcTouchDrag
|
||||
});
|
||||
toolManager.addTool('zoom', {
|
||||
mouse: cornerstoneTools.zoom,
|
||||
touch: cornerstoneTools.zoomTouchDrag
|
||||
});
|
||||
toolManager.addTool('wwwcRegion', {
|
||||
mouse: cornerstoneTools.wwwcRegion,
|
||||
touch: cornerstoneTools.wwwcRegionTouch
|
||||
});
|
||||
toolManager.addTool('dragProbe', {
|
||||
mouse: cornerstoneTools.dragProbe,
|
||||
touch: cornerstoneTools.dragProbeTouch
|
||||
});
|
||||
toolManager.addTool('pan', {
|
||||
mouse: cornerstoneTools.pan,
|
||||
touch: cornerstoneTools.panTouchDrag
|
||||
});
|
||||
toolManager.addTool('stackScroll', {
|
||||
mouse: cornerstoneTools.stackScroll,
|
||||
touch: cornerstoneTools.stackScrollTouchDrag
|
||||
});
|
||||
toolManager.addTool('length', {
|
||||
mouse: cornerstoneTools.length,
|
||||
touch: cornerstoneTools.lengthTouch
|
||||
});
|
||||
toolManager.addTool('angle', {
|
||||
mouse: cornerstoneTools.simpleAngle,
|
||||
touch: cornerstoneTools.simpleAngleTouch
|
||||
});
|
||||
toolManager.addTool('magnify', {
|
||||
mouse: cornerstoneTools.magnify,
|
||||
touch: cornerstoneTools.magnifyTouchDrag
|
||||
});
|
||||
toolManager.addTool('ellipticalRoi', {
|
||||
mouse: cornerstoneTools.ellipticalRoi,
|
||||
touch: cornerstoneTools.ellipticalRoiTouch
|
||||
});
|
||||
toolManager.addTool('rectangleRoi', {
|
||||
mouse: cornerstoneTools.rectangleRoi,
|
||||
touch: cornerstoneTools.rectangleRoiTouch
|
||||
});
|
||||
toolManager.addTool('annotate', {
|
||||
mouse: cornerstoneTools.arrowAnnotate,
|
||||
touch: cornerstoneTools.arrowAnnotateTouch
|
||||
});
|
||||
|
||||
if (OHIF.viewer.defaultTool) {
|
||||
activeTool = OHIF.viewer.defaultTool;
|
||||
}
|
||||
|
||||
configureTools();
|
||||
initialized = true;
|
||||
},
|
||||
addTool: function(name, base) {
|
||||
tools[name] = base;
|
||||
},
|
||||
getTools: function() {
|
||||
return tools;
|
||||
},
|
||||
setToolDefaultStates: function(states) {
|
||||
toolDefaultStates = states;
|
||||
},
|
||||
getToolDefaultStates: function() {
|
||||
return toolDefaultStates;
|
||||
},
|
||||
setActiveToolForElement: function(tool, element) {
|
||||
var canvases = $(element).find('canvas');
|
||||
if (element.classList.contains('empty') || !canvases.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// First, deactivate the current active tool
|
||||
tools[activeTool].mouse.deactivate(element, 1);
|
||||
|
||||
if (tools[activeTool].touch) {
|
||||
tools[activeTool].touch.deactivate(element);
|
||||
}
|
||||
|
||||
// Enable tools based on their default states
|
||||
Object.keys(toolDefaultStates).forEach(function(action) {
|
||||
var relevantTools = toolDefaultStates[action];
|
||||
if (!relevantTools || !relevantTools.length || action === 'disabledToolButtons') {
|
||||
return;
|
||||
}
|
||||
|
||||
relevantTools.forEach(function(toolType) {
|
||||
if ((action === 'activate') ||
|
||||
(action === 'deactivate')) {
|
||||
tools[toolType].mouse[action](element, 1);
|
||||
} else {
|
||||
tools[toolType].mouse[action](element);
|
||||
}
|
||||
|
||||
tools[toolType].touch[action](element);
|
||||
});
|
||||
});
|
||||
|
||||
// Get the stack toolData
|
||||
var toolData = cornerstoneTools.getToolState(element, 'stack');
|
||||
if (!toolData || !toolData.data || !toolData.data.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the imageIds for this element
|
||||
var imageIds = toolData.data[0].imageIds;
|
||||
|
||||
// Deactivate all the middle mouse, right click, and scroll wheel tools
|
||||
cornerstoneTools.pan.deactivate(element);
|
||||
cornerstoneTools.zoom.deactivate(element);
|
||||
cornerstoneTools.zoomWheel.deactivate(element);
|
||||
cornerstoneTools.stackScrollWheel.deactivate(element);
|
||||
|
||||
// Reactivate the relevant scrollwheel tool for this element
|
||||
if (imageIds.length > 1) {
|
||||
// scroll is the default tool for middle mouse wheel for stacks
|
||||
cornerstoneTools.stackScrollWheel.activate(element);
|
||||
} else {
|
||||
// zoom is the default tool for middle mouse wheel for single images (non stacks)
|
||||
cornerstoneTools.zoomWheel.activate(element);
|
||||
}
|
||||
|
||||
// This block ensures that the middle mouse and scroll tools keep working
|
||||
if (tool === 'pan') {
|
||||
cornerstoneTools.pan.activate(element, 3); // 3 means left mouse button and middle mouse button
|
||||
cornerstoneTools.zoom.activate(element, 4); // zoom is the default tool for right mouse button
|
||||
} else if (tool === 'zoom') {
|
||||
cornerstoneTools.pan.activate(element, 2); // pan is the default tool for middle mouse button
|
||||
cornerstoneTools.zoom.activate(element, 5); // 5 means left mouse button and right mouse button
|
||||
} else {
|
||||
// Reactivate the middle mouse and right click tools
|
||||
cornerstoneTools.pan.activate(element, 2); // pan is the default tool for middle mouse button
|
||||
cornerstoneTools.zoom.activate(element, 4); // zoom is the default tool for right mouse button
|
||||
|
||||
// Activate the chosen tool
|
||||
tools[tool].mouse.activate(element, 1);
|
||||
}
|
||||
|
||||
if (tools[tool].touch) {
|
||||
tools[tool].touch.activate(element);
|
||||
}
|
||||
|
||||
cornerstoneTools.zoomTouchPinch.activate(element);
|
||||
cornerstoneTools.panMultiTouch.activate(element);
|
||||
},
|
||||
setActiveTool: function(tool, elements) {
|
||||
if (!initialized) {
|
||||
toolManager.init();
|
||||
}
|
||||
|
||||
if (!tool) {
|
||||
tool = defaultTool;
|
||||
}
|
||||
|
||||
if (!elements || !elements.length) {
|
||||
elements = $('.imageViewerViewport');
|
||||
}
|
||||
|
||||
// Otherwise, set the active tool for all viewport elements
|
||||
$(elements).each(function(index, element) {
|
||||
toolManager.setActiveToolForElement(tool, element);
|
||||
});
|
||||
activeTool = tool;
|
||||
|
||||
// Store the active tool in the session in order to enable reactivity
|
||||
Session.set('ToolManagerActiveTool', tool);
|
||||
},
|
||||
getActiveTool: function() {
|
||||
if (!activeTool) {
|
||||
activeTool = defaultTool;
|
||||
}
|
||||
|
||||
return activeTool;
|
||||
},
|
||||
setDefaultTool: function(tool) {
|
||||
defaultTool = tool;
|
||||
},
|
||||
getDefaultTool: function() {
|
||||
return defaultTool;
|
||||
}
|
||||
|
||||
};
|
||||
@@ -1,6 +0,0 @@
|
||||
updateAllViewports = function() {
|
||||
var viewports = $('.imageViewerViewport').not('.empty');
|
||||
viewports.each(function(index, element) {
|
||||
cornerstone.updateImage(element);
|
||||
});
|
||||
};
|
||||
@@ -1,205 +0,0 @@
|
||||
import { Session } from 'meteor/session';
|
||||
import { $ } from 'meteor/jquery';
|
||||
|
||||
getActiveViewportElement = () => {
|
||||
const viewportIndex = Session.get('activeViewport') || 0;
|
||||
return $('.imageViewerViewport').get(viewportIndex);
|
||||
};
|
||||
|
||||
zoomIn = () => {
|
||||
const element = getActiveViewportElement();
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
let viewport = cornerstone.getViewport(element);
|
||||
const scaleIncrement = 0.15;
|
||||
const maximumScale = 10;
|
||||
viewport.scale = Math.min(viewport.scale + increment, maximumScale);
|
||||
cornerstone.setViewport(element, viewport);
|
||||
};
|
||||
|
||||
zoomOut = () => {
|
||||
const element = getActiveViewportElement();
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
let viewport = cornerstone.getViewport(element);
|
||||
const scaleIncrement = 0.15;
|
||||
const minimumScale = 0.05;
|
||||
viewport.scale = Math.max(viewport.scale - increment, minimumScale);
|
||||
cornerstone.setViewport(element, viewport);
|
||||
};
|
||||
|
||||
zoomToFit = () => {
|
||||
const element = getActiveViewportElement();
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
cornerstone.fitToWindow(element);
|
||||
};
|
||||
|
||||
rotateL = () => {
|
||||
const element = getActiveViewportElement();
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
let viewport = cornerstone.getViewport(element);
|
||||
viewport.rotation -= 90;
|
||||
cornerstone.setViewport(element, viewport);
|
||||
updateOrientationMarkers(element, viewport);
|
||||
};
|
||||
|
||||
rotateR = () => {
|
||||
const element = getActiveViewportElement();
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
let viewport = cornerstone.getViewport(element);
|
||||
viewport.rotation += 90;
|
||||
cornerstone.setViewport(element, viewport);
|
||||
updateOrientationMarkers(element, viewport);
|
||||
};
|
||||
|
||||
invert = () => {
|
||||
const element = getActiveViewportElement();
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
let viewport = cornerstone.getViewport(element);
|
||||
viewport.invert = (viewport.invert === false);
|
||||
cornerstone.setViewport(element, viewport);
|
||||
};
|
||||
|
||||
flipV = () => {
|
||||
const element = getActiveViewportElement();
|
||||
let viewport = cornerstone.getViewport(element);
|
||||
viewport.vflip = (viewport.vflip === false);
|
||||
cornerstone.setViewport(element, viewport);
|
||||
updateOrientationMarkers(element, viewport);
|
||||
};
|
||||
|
||||
flipH = () => {
|
||||
const element = getActiveViewportElement();
|
||||
let viewport = cornerstone.getViewport(element);
|
||||
viewport.hflip = (viewport.hflip === false);
|
||||
cornerstone.setViewport(element, viewport);
|
||||
updateOrientationMarkers(element, viewport);
|
||||
};
|
||||
|
||||
resetViewport = () => {
|
||||
const element = getActiveViewportElement();
|
||||
const enabledElement = cornerstone.getEnabledElement(element);
|
||||
if (enabledElement.fitToWindow === false) {
|
||||
const imageId = enabledElement.image.imageId;
|
||||
const instance = cornerstoneTools.metaData.get('instance', imageId);
|
||||
|
||||
enabledElement.viewport = cornerstone.getDefaultViewport(enabledElement.canvas, enabledElement.image);
|
||||
|
||||
const instanceClassDefaultViewport = getInstanceClassDefaultViewport(instance, enabledElement, imageId);
|
||||
cornerstone.setViewport(element, instanceClassDefaultViewport);
|
||||
} else {
|
||||
cornerstone.reset(element);
|
||||
}
|
||||
};
|
||||
|
||||
clearTools = () => {
|
||||
const element = getActiveViewportElement();
|
||||
const toolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager;
|
||||
toolStateManager.clear(element);
|
||||
cornerstone.updateImage(element);
|
||||
};
|
||||
|
||||
// Toggle the play/stop state for the cornerstone clip tool
|
||||
toggleCinePlay = () => {
|
||||
// Get the active viewport element
|
||||
const element = getActiveViewportElement();
|
||||
|
||||
// Check if it's playing the clip to toggle it
|
||||
if (isPlaying()) {
|
||||
cornerstoneTools.stopClip(element);
|
||||
} else {
|
||||
cornerstoneTools.playClip(element);
|
||||
}
|
||||
|
||||
// Update the UpdateCINE session property
|
||||
Session.set('UpdateCINE', Random.id());
|
||||
};
|
||||
|
||||
// Show/hide the CINE dialog
|
||||
toggleCineDialog = () => {
|
||||
const cineDialog = document.getElementById('cineDialog');
|
||||
toggleDialog(cineDialog);
|
||||
};
|
||||
|
||||
// Check if the clip is playing on the active viewport
|
||||
isPlaying = () => {
|
||||
// Create a dependency on LayoutManagerUpdated and UpdateCINE session
|
||||
Session.get('UpdateCINE');
|
||||
Session.get('LayoutManagerUpdated');
|
||||
|
||||
// Get the viewport element and its current playClip tool state
|
||||
const element = getActiveViewportElement();
|
||||
// Empty Elements throws cornerstore exception
|
||||
if (!element || !$(element).find('canvas').length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const toolState = cornerstoneTools.getToolState(element, 'playClip');
|
||||
|
||||
// Stop here if the tool state is not defined yet
|
||||
if (!toolState) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the clip state
|
||||
const clipState = toolState.data[0];
|
||||
|
||||
if(clipState) {
|
||||
// Return true if the clip is playing
|
||||
return !_.isUndefined(clipState.intervalId);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// Check if a study has multiple frames
|
||||
hasMultipleFrames = () => {
|
||||
// Its called everytime active viewport and/or layout change
|
||||
Session.get('activeViewport');
|
||||
Session.get('LayoutManagerUpdated');
|
||||
|
||||
const activeViewport = getActiveViewportElement();
|
||||
|
||||
// No active viewport yet: disable button
|
||||
if(!activeViewport || !$(activeViewport).find('canvas').length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get images in the stack
|
||||
const stackToolData = cornerstoneTools.getToolState(activeViewport, 'stack');
|
||||
|
||||
// No images in the stack, so disable button
|
||||
if (!stackToolData || !stackToolData.data || !stackToolData.data.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get number of images in the stack
|
||||
const stackData = stackToolData.data[0];
|
||||
const nImages = stackData.imageIds && stackData.imageIds.length ? stackData.imageIds.length : 1;
|
||||
|
||||
// Stack has just one image, so disable button
|
||||
if(nImages === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// Create an event listener to update playing state when a clip stops playing
|
||||
$(window).on('CornerstoneToolsClipStopped', () => Session.set('UpdateCINE', Random.id()));
|
||||
Reference in new issue
Block a user