@galaxyproject/nora
Version:
NORA Medical Imaging Viewer
1,479 lines (1,210 loc) • 353 kB
JavaScript
/**
* @module MiscFunctions
*/
var resources = {
lang: "en",
tooltips: {
en: {
roitool_open: "open roitool",
masterviewport: "use gridding of content as interpolation master",
changeslicing: "change slicing",
centerview: "center view at current location",
resetclims: "reset colormap limits",
changecolormap: "select a different colormap",
switchto3d: "Switch to 3D mode",
zoomviewport: "Zoom viewport",
closeviewport: "close viewport",
dragdropviewport: "drag and drop content",
closetool: "close the tool",
resizetoolvertically: "resize the tool vertically",
dragdroptool: "drag and drop the tool",
patienttrash: "delete selected items or drop an elemet to delete",
refreshtable: "refresh the patient table",
anonymmode: "(un)switch table anonymization",
levelswitcher: "switch table mode",
singlesel: "switch to single patient mode",
tabletoggler: "resize/toggle table",
closeOVL: "close overlay view",
closefiberview: "close fiber viewer",
createvisitmap: "create a visitmap of current selection",
showallfibers: "show all fibers",
fiberpick: "control fiber picker (use annotations to select fibers)",
fibercut: "cut fibers to imaging planes",
cropfibers: "create fiber subset by cropping current selection",
currentpicker: "enable fiberset for picking",
batchtestmode: "send jobs to your matlab console",
isosurfROI: "enable 3D isosurface view of ROI",
jumptoROI: "jump to center of ROI",
selectcolor: "select a different color",
saveuploadROI: "upload/save ROI",
closeROI: "close view of ROI",
createroi: "miscellaneous overlay",
createemptyroi: "create empty ROI",
makecurrent: "enable ROI for drawing",
playstoptimeseries: "play or stop timeseries movie",
showhide: "show/hide content",
drawonlyonsimilarcolors: "draw on colors similar to center voxel (use colormap limits to control sensitivity)",
regionfillsimilarcolors: "unrestricted region filling (use colormap limits and mouse left/right to control sensitivity)",
regionfillwithinpen: "region filling within pen",
misctools: "miscellaneous tools",
mcpsys: "create mcp reorientation system from two/three markers",
addnewanno: "add new marker to annotation",
closeanno: "remove this annotation",
showhideanno: "show/hide annotation",
jumptopoint: "jump to this point",
delannopoint: "delete this point",
}
}
};
if (typeof jQuery != "undefined")
{
$.prototype.appendTooltip = function(id)
{
var tout;
var fadeOuttimer = {
clear: function() {
clearInterval(this.id)
},
callback: function()
{
inhibit();
}
};
this.on('mouseenter', function(event) {
fadeOuttimer.id = setInterval(function() {
fadeOuttimer.callback()
}, 8000);
clearInterval(tout)
tout = setTimeout(function() {
$("#KJobinfoTooltip").remove();
var ttips = resources.tooltips[resources.lang];
var text
if (typeof id == "function")
text = id();
else
text = ttips[id];
if (text == undefined)
text = id;
var $div = $("<div id='standardTooltip'> " + text + " </div>");
$div.css("top", event.clientY);
$div.css("left", event.clientX + 15);
$div.appendTo($(document.body));
if ($div.position().left + $div.width() > $(document.body).width())
{
$div.css('left', $(document.body).width() - $div.width() - 10);
$div.css('top', $div.position().top + 15);
}
if ($div.position().top + $div.height() > $(document.body).height())
$div.css('top', $(document.body).height() - $div.height() - 10);
$div.hide();
$div.fadeIn(200);
}, 800);
});
var inhibit = function()
{
fadeOuttimer.clear();
clearInterval(tout);
$("#standardTooltip").remove();
}
this.on('mouseleave mouseclick mousedown', inhibit);
return this;
}
}
/*
* Make a function that calculates the distance squared of (x, y, z) to the center of the sphere at (0, 0, 0) or to the center of the cylinder (0, 0)
* @param sx2: squared scaling factor for x.
* @param sy2: squared scaling factor for y.
* @param sz2: squared scaling factor for z.
* @param slicing (int): in case of tool shape cylinder, the dimension the cylinder extends over
* @param shape (string): "sphere", "cylinder", the shape of the tool
* @return (function(x, y, z))
*/
function makefunction_distance_to_center_2(sx2, sy2, sz2, slicing, shape)
{
var distance_to_center_2;
if (shape == "cylinder")
{
if (slicing == 0) // to get a cylinder, calculate the distance to the center only with two dims. the dims depend on the view the user clicked on.
distance_to_center_2 = function(x, y, z) { return y * y * sy2 + z * z * sz2; };
else if (slicing == 1)
distance_to_center_2 = function(x, y, z) { return x * x * sx2 + z * z * sz2; };
else if (slicing == 2)
distance_to_center_2 = function(x, y, z) { return x * x * sx2 + y * y * sy2; };
}
else if (shape == "sphere") // regular sphere shaped tool
{
distance_to_center_2 = function(x, y, z) { return x * x * sx2 + y * y * sy2 + z * z * sz2; };
}
else
{
throw Error("Invalid tool shape.");
}
return distance_to_center_2;
}
function zeroPad(num, places) {
var zero = places - num.toString().length + 1;
return Array(+(zero > 0 && zero)).join("0") + num;
}
if (typeof alertify != "undefined")
alertify.lazy_error = function(errstr,type,delay)
{
if (delay == undefined)
delay = 5000;
if (!alertify[type])
{
alertify.error(errstr);
alertify[type] = true;
setTimeout(function() { alertify[type] = false;},delay);
}
}
if (Array.prototype.chunk == undefined)
{
Array.prototype.chunk =
Object.defineProperty(Array.prototype, 'chunk', {
value:
function(fn, chunksize, delay, aggregate, onready)
{
var forchunk = function(_this, fn, chunksize, delay)
{
if (delay == undefined)
delay = 0;
_this.interval_id = setInterval(function(_this) {
return function()
{
if (_this.cnt == undefined)
_this.cnt = 0;
for (var k = 0; k < chunksize & k + _this.cnt < _this.length; k++)
{
var resval = fn(_this[k + _this.cnt], k + _this.cnt, _this);
}
if (aggregate)
{
aggregate(_this.cnt);
}
_this.cnt += chunksize;
if (_this.cnt >= _this.length | resval === false)
{
clearInterval(_this.interval_id);
delete _this.cnt;
delete _this.interval_id;
if (onready != undefined)
onready();
}
}
}(_this), delay);
}
;
forchunk(this, fn, chunksize, delay);
}
});
//Float32Array.prototype.chunk =
Object.defineProperty(Float32Array.prototype, 'chunk', {
value:
function(fn, chunksize, delay, aggregate, onready)
{
var forchunk = function(_this, fn, chunksize, delay)
{
if (delay == undefined)
delay = 0;
_this.interval_id = setInterval(function(_this) {
return function()
{
if (_this.cnt == undefined)
_this.cnt = 0;
for (var k = 0; k < chunksize & k + _this.cnt < _this.length; k++)
{
fn(_this[k + _this.cnt], k + _this.cnt, _this);
}
if (aggregate)
{
aggregate(_this.cnt);
}
_this.cnt += chunksize;
if (_this.cnt >= _this.length)
{
clearInterval(_this.interval_id);
delete _this.cnt;
delete _this.interval_id;
if (onready != undefined)
onready();
}
}
}(_this), delay);
}
;
forchunk(this, fn, chunksize, delay);
}
}
);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////// Octree
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** The octree used for fast position lookup of fibers
* @class
*/
function Octree(position, size, accuracy) {
this.maxDistance = Math.max(size[0], Math.max(size[1], size[2]));
this.accuracy = 0;
this.root = new Octree.Cell(this,position,size,0);
this.numPoints = 0;
}
Octree.fromBoundingBox = function(bbox) {
return new Octree(bbox.min.clone(),bbox.getSize().clone());
}
;
Octree.MaxLevel = 4;
Octree.prototype.add = function(p, data) {
this.numPoints++;
this.root.add(p, data);
}
;
Octree.prototype.has = function(p) {
return this.root.has(p);
}
;
Octree.prototype.findNearestPoint = function(p, options) {
options.includeData = options.includeData ? options.includeData : false;
options.bestDist = options.maxDist ? options.maxDist : Infinity;
options.notSelf = options.notSelf ? options.notSelf : false;
var result = this.root.findNearestPoint(p, options);
if (result) {
if (options.includeData)
return result;
else
return result.point;
}
else
return null ;
}
;
Octree.prototype.findNearbyPoints = function(p, r, options) {
options = options || {};
var result = {
points: [],
data: []
};
this.root.findNearbyPoints(p, r, result, options);
return result;
}
;
Octree.prototype.getAllCellsAtLevel = function(cell, level, result) {
if (typeof level == 'undefined') {
level = cell;
cell = this.root;
}
result = result || [];
if (cell.level == level) {
if (cell.points.length > 0) {
result.push(cell);
}
return result;
} else {
cell.children.forEach(function(child) {
this.getAllCellsAtLevel(child, level, result);
}
.bind(this));
return result;
}
}
;
Octree.Cell = function(tree, position, size, level) {
this.tree = tree;
this.position = position;
this.size = size;
this.level = level;
this.points = [];
this.data = [];
this.children = [];
}
;
Octree.Cell.prototype.has = function(p) {
if (!this.contains(p))
return null ;
if (this.children.length > 0) {
for (var i = 0; i < this.children.length; i++) {
var duplicate = this.children[i].has(p);
if (duplicate) {
return duplicate;
}
}
return null ;
} else {
var minDistSqrt = this.tree.accuracy * this.tree.accuracy;
for (var i = 0; i < this.points.length; i++) {
var o = this.points[i];
var distSq = (p[0] - o[0]) * (p[0] - o[0]) + (p[1] - o[1]) * (p[1] - o[1]) + (p[2] - o[2]) * (p[2] - o[2]);
if (distSq <= minDistSqrt) {
return o;
}
}
return null ;
}
}
;
Octree.Cell.prototype.add = function(p, data) {
if (this.children.length > 0) {
this.addToChildren(p, data);
} else {
this.points.push(p);
this.data.push(data);
if (this.points.length > 10 && this.level < Octree.MaxLevel) {
this.split();
}
}
}
;
Octree.Cell.prototype.addToChildren = function(p, data) {
for (var i = 0; i < this.children.length; i++) {
if (this.children[i].contains(p)) {
this.children[i].add(p, data);
break;
}
}
}
;
Octree.Cell.prototype.contains = function(p) {
return p[0] >= this.position[0] - this.tree.accuracy
&& p[1] >= this.position[1] - this.tree.accuracy
&& p[2] >= this.position[2] - this.tree.accuracy
&& p[0] < this.position[0] + this.size[0] + this.tree.accuracy
&& p[1] < this.position[1] + this.size[1] + this.tree.accuracy
&& p[2] < this.position[2] + this.size[2] + this.tree.accuracy;
}
;
Octree.Cell.prototype.split = function() {
var x = this.position[0];
var y = this.position[1];
var z = this.position[2];
var w2 = this.size[0] / 2;
var h2 = this.size[1] / 2;
var d2 = this.size[2] / 2;
var whd = [w2, h2, d2];
this.children.push(new Octree.Cell(this.tree,[x, y, z],whd,this.level + 1));
this.children.push(new Octree.Cell(this.tree,[x + w2, y, z],whd,this.level + 1));
this.children.push(new Octree.Cell(this.tree,[x, y, z + d2],whd,this.level + 1));
this.children.push(new Octree.Cell(this.tree,[x + w2, y, z + d2],whd,this.level + 1));
this.children.push(new Octree.Cell(this.tree,[x, y + h2, z],whd,this.level + 1));
this.children.push(new Octree.Cell(this.tree,[x + w2, y + h2, z],whd,this.level + 1));
this.children.push(new Octree.Cell(this.tree,[x, y + h2, z + d2],whd,this.level + 1));
this.children.push(new Octree.Cell(this.tree,[x + w2, y + h2, z + d2],whd,this.level + 1));
for (var i = 0; i < this.points.length; i++) {
this.addToChildren(this.points[i], this.data[i]);
}
this.points = [];
this.data = [];
}
;
Octree.Cell.prototype.squareDistanceToCenter = function(p) {
var dx = p[0] - (this.position[0] + this.size[0] / 2);
var dy = p[1] - (this.position[1] + this.size[1] / 2);
var dz = p[2] - (this.position[2] + this.size[2] / 2);
return dx * dx + dy * dy + dz * dz;
}
Octree.Cell.prototype.findNearestPoint = function(p, options) {
var nearest = null ;
var nearestData = null ;
var bestDist = options.bestDist;
if (this.points.length > 0 && this.children.length == 0) {
for (var i = 0; i < this.points.length; i++) {
var dist = this.points[i].distance(p);
if (dist <= bestDist) {
if (dist == 0 && options.notSelf)
continue;
bestDist = dist;
nearest = this.points[i];
nearestData = this.data[i];
}
}
}
var children = this.children;
var children = this.children
.map(function(child) {
return {
child: child,
dist: child.squareDistanceToCenter(p)
}
})
.sort(function(a, b) {
return a.dist - b.dist;
})
.map(function(c) {
return c.child;
});
if (children.length > 0) {
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (child.points.length > 0) {
if (p[0] < child.position[0] - bestDist || p[0] > child.position[0] + child.size[0] + bestDist ||
p[1] < child.position[1] - bestDist || p[1] > child.position[1] + child.size[1] + bestDist ||
p[2] < child.position[2] - bestDist || p[2] > child.position[2] + child.size[2] + bestDist
) {
continue;
}
var childNearest = child.findNearestPoint(p, options);
if (!childNearest || !childNearest.point) {
continue;
}
var childNearestDist = childNearest.point.distance(p);
if (childNearestDist < bestDist) {
nearest = childNearest.point;
bestDist = childNearestDist;
nearestData = childNearest.data;
}
}
}
}
return {
point: nearest,
data: nearestData
}
}
;
Octree.Cell.prototype.findNearbyPoints = function(p, r, result, options) {
for (var i = 0; i < this.points.length; i++) {
var dx = this.points[i][0] - p[0];
var dy = this.points[i][1] - p[1];
var dz = this.points[i][2] - p[2];
var dist = (dx * dx + dy * dy + dz * dz);
if (dist <= r * r) {
if (dist == 0 && options.notSelf)
continue;
result.points.push(this.points[i]);
if (options.includeData)
result.data.push(this.data[i]);
}
}
var children = this.children
if (children.length > 0) {
for (var i = 0; i < children.length; i++) {
var child = children[i];
//if (child.points.length > 0)
{
if (p[0] < child.position[0] - r || p[0] > child.position[0] + child.size[0] + r ||
p[1] < child.position[1] - r || p[1] > child.position[1] + child.size[1] + r ||
p[2] < child.position[2] - r || p[2] > child.position[2] + child.size[2] + r
) {
continue;
}
child.findNearbyPoints(p, r, result, options);
}
}
}
}
;
function getWorkerScriptAsBlob(scriptName)
{
if (scriptStore[scriptName] == undefined)
{
console.error("script " +scriptName+ " not found")
return;
}
var r = /importScripts\([\"\'](?<id>[\w\/\.]+.js)[\"\']\)/g;
var code = '';
var m;
var str = scriptStore[scriptName];
while ((m = RegExp(r).exec(str)) !== null)
{
var id = m.groups.id
if (id.substring(0,3) == '../')
id = id.substring(3);
if (scriptStore[id] == undefined)
console.log("script " +id+ " not found")
code += scriptStore[id] + "\n";
}
code += scriptStore[scriptName];
blob = new Blob([code], {type: 'application/javascript'});
return URL.createObjectURL(blob)
}
function startWorker(scriptname)
{
if (typeof url_pref != "undefined")
{
if (url_pref == "import:")
scriptname = getWorkerScriptAsBlob(scriptname)
else
scriptname = url_pref + scriptname+ '?' + static_info.softwareversion;
}
return new Worker(scriptname);
}
executeImageWorker.running_workers = 0;
function executeImageWorker(execObj,Buffers,progress,onready,worker)
{
if (worker == undefined)
{
worker = startWorker('KImageProcWorker.js');
worker.postMessage = worker.webkitPostMessage || worker.postMessage;
worker.addEventListener('message', function(e) {
e = e.data;
if (e.msg == 'done')
{
if (progress != undefined)
progress();
executeImageWorker.running_workers--;
onready(e);
}
else
if (progress != undefined)
progress(e.msg);
}, false);
worker.kill = function()
{
executeImageWorker.running_workers--;
worker.postMessage({'msg':'kill'},[]);
}
}
executeImageWorker.running_workers++;
setTimeout(function() {
try{
worker.postMessage(execObj,Buffers); // Send data to our worker.
} catch (err)
{
console.log(execObj)
throw err
}
},250);
return worker;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////// generic context menu
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** A generic contextmenu
* @param {function} themenu - A function returning an "ul" containing the menu
* @param {function} theselfun - called on menu selection with signature theselfun(onchoice,mouseupevent,mousedownevent). First argument contains onchoice attribute of menu "li"
* @param {logical} loose - ??
* @param {logical} keepOpenAfterClick - menu disappears only on mouse leave
* @param {logical} selectonrelease - allow item selection on moushold- mouseup, can be dangerous for unexperienced users
* @return {function} - a menu creater function that should be called upon click/mousedown etc.
*/
function KContextMenu(themenu, theselfun, loose, keepOpenAfterClick, eventonmenu, selectonmouseup)
{
var last_ev;
var createFun = function(ev)
{
if (ev != undefined)
{
ev.preventDefault();
ev.stopPropagation();
}
last_ev = ev;
var target = ev.target;
var $cmdiv = $("<div class='patientTableContextmenu'>");
var mymenu = themenu(ev);
if (mymenu == undefined)
return;
// this was always called twice, with (ev)...?
//var $menu = themenu(ev).appendTo($cmdiv);
var $menu = mymenu.appendTo($cmdiv);
// correct for chrome bug, where a hover is not triggered during mousedown
$menu.find("li").each(function(i, a) {
$(a).mouseenter(function() {
$(this).addClass('jsHover');
})
.mouseleave(function() {
$(this).removeClass('jsHover');
})
});
if(keepOpenAfterClick)
{
var offs_top = -10;
var offs_left = -5;
}
else
{
var offs_top = 5;
var offs_left = 5;
}
$cmdiv.css("display", "block");
$cmdiv.css({
left: ev.pageX + offs_left,
top: ev.pageY + offs_top
});
$cmdiv.show();
var selFun = function(ev2)
{
var str;
var $target = $(ev2.target)
for (var k = 0; k < 3; k++)
{
str = $target.attr("onchoice")
if (str != undefined)
break;
else
$target = $target.parent();
}
if (str != 'preventSelection' && !$target.hasClass("inactive"))
{
ev2.preventDefault();
ev2.stopPropagation();
if (str != undefined | !loose) // (keepOpenAfterClick | !loose ) // | ev2.type == "mousedown" | ev2.type == "mouseup") )
{
$cmdiv.remove();
fadeOuttimer.clear();
$(document.body).off("mouseup mousedown");
}
if (theselfun(str, ev2, ev) == "close")
return;
if (keepOpenAfterClick)
{
createFun(last_ev);
}
}
}
;
$(document.body).append($cmdiv);
/*
var uls = $cmdiv.find("ul,div");
var left = 0;
for (var k = 0; k < uls.length;k++)
{
var $ul = $(uls[k]);
var disp = $ul.css('display');
if ($ul.offset().left +left + $ul.width() > $(document.body).width())
$ul.css('left',-($ul.offset().left +left + $ul.width() - $(document.body).width())- offs_left-20);
if ($(document.body).height() != 0 && $ul.offset().top + $ul.height() > $(document.body).height())
$ul.css('top', -($ul.offset().top + $ul.height() - $(document.body).height())- offs_top-10);
if (k==0)
left = $ul.offset().left + $ul.width()-20 ;
}
*/
var disp = $cmdiv.css('display');
if ($cmdiv.offset().left + $cmdiv.width() > $(document.body).width())
$cmdiv.css('left', -( $cmdiv.width() - $(document.body).width())- offs_left-5);
if ($(document.body).height() != 0 && $cmdiv.offset().top + $cmdiv.height() > $(document.body).height())
$cmdiv.css('top', -( $cmdiv.height() - $(document.body).height())- offs_top-10);
var $which = $(document.body);
if (eventonmenu)
{
$which = $cmdiv;
$(document.body).on("mousedown",function()
{
$(document.body).off("mousedown");
$cmdiv.off("mouseup mousedown");
$cmdiv.remove();
})
}
if (keepOpenAfterClick || selectonmouseup === false )
$which.on("mousedown", selFun);
else
$which.on("mouseup mousedown", selFun);
$which.on("contextmenu",function(e)
{
e.preventDefault();
});
$cmdiv.on("mouseleave", function(ev) {
$which.off("mouseup mousedown");
fadeOuttimer.clear();
$cmdiv.remove();
});
var fadeOuttimer = {
clear: function() {
clearInterval(this.id)
},
callback: function()
{
this.clear();
$which.off("mouseup mousedown");
$cmdiv.fadeOut(1000);
}
};
fadeOuttimer.id = setInterval(function() {
fadeOuttimer.callback()
}, 10000000);
}
return createFun;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////// shared links
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** open a shared link. Information comes from php within "sharedLink" variable
* @param {function} onready - called after establishment of shared viewing state
*/
function openSharedLink(sharedLink,onready)
{
// extend the state with the shared link. Can be done now safely, as the presets are not overwritten
// dangerous. can destroy project state and everything ...
//$.extend(state, share dLink, true);
// etabslish tree state
if (userinfo.username != guestuser && sharedLink.project != undefined)
{
if (sharedLink.onlyContent)
{
// set settings
if (sharedLink.ViewerSettings !== undefined)
{
state.viewer = sharedLink.ViewerSettings;
stateManager.applyState(state);
}
sharedLink.project = currentModule;
loadSharedContent()
}
else
selectProject(sharedLink.project, function() {
// set settings
if (sharedLink.ViewerSettings !== undefined)
{
state.viewer = sharedLink.ViewerSettings;
stateManager.applyState(state);
}
state.search_row = sharedLink.search_row;
state.search_row_a = sharedLink.search_row_a;
switchto(state.viewer.selectionMode,undefined,function()
{
patientTableMirror.nodesExpanded = sharedLink.expandedNodes || [];
patientTableMirror.selectedItems = sharedLink.selectedItems || [];
patientTableMirror.mirrorState();
currentPSID = sharedLink.currentPSID || {};
if (currentPSID.patients_id)
{
if (state.viewer.selectionMode[1] == 's')
{
setEditModeText(currentPSID.patients_id + riddelim + currentPSID.studies_id);
}
if (state.viewer.selectionMode[1] == 'p')
{
setEditModeText(currentPSID.patients_id);
}
}
loadSharedContent();
});
});
}
else
{
// set settings
if (sharedLink.ViewerSettings !== undefined)
{
state.viewer = sharedLink.ViewerSettings;
stateManager.applyState(state);
}
loadSharedContent();
}
function loadSharedContent()
{
$(document.body).addClass("wait");
if (sharedLink.toolstate)
KToolWindow.reestablishToolState(sharedLink.toolstate);
if (sharedLink.naviMode != undefined)
{
KViewer.navigationTool.switchToNavimode( sharedLink.naviMode);
if (sharedLink.navi_reorientationMatrix!= undefined && sharedLink.navi_reorientationMatrix.notID)
{
KViewer.reorientationMatrix.notID = true;
var transform = sharedLink.navi_reorientationMatrix;
KViewer.reorientationMatrix.name = transform.name;
KViewer.reorientationMatrix.matrix = math.matrix(transform.matrix);
KViewer.navigationTool.transform.update();
}
//KViewer.reorientationMatrix = sharedLink.navi_reorientationMatrix;
}
// load annos
if (sharedLink.annotations != undefined)
{
if (markerProxy != undefined)
{
markerProxy.delAll();
markerProxy.import(sharedLink.annotations.content);
if (sharedLink.annotations.panelenabled)
{
for(var p in markerProxy.markersets)
markerProxy.markersets[p].showPanel();
}
}
}
// load atlas deffield
if (sharedLink.atlas_defField != undefined)
KViewer.dataManager.loadData({
URLType: 'serverfile',
fileID: sharedLink.atlas_defField,
json: {
project: sharedLink.project
},
callback: KViewer.atlasTool.addAtlas
});
if (sharedLink.fmri_tmodes != undefined)
KViewer.dataManager.loadData({
URLType: 'serverfile',
fileID: sharedLink.fmri_tmodes
});
if (sharedLink.dummyNifti_params)
{
var dumpa = sharedLink.dummyNifti_params
KViewer.lastDummyNifti = createDummyNifti(dumpa.sizes,dumpa.bbox_max,dumpa.bbox_min,dumpa.perm,dumpa.flip)
}
// load viewport content
var params = sharedLink.viewports || [];
var q = [];
var derived_q = [];
if (sharedLink.atlases != undefined & !electron)
for (var k = 0; k < sharedLink.atlases.length; k++)
{
var param = ({ URLType: 'serverfile', fileID:sharedLink.atlases[k].fileID,
json:{project:sharedLink.atlases[k].project},intent:{atlas:true,project:sharedLink.atlases[k].project}});
if (param.json.project == undefined)
param.json.project = sharedLink.project
q.push(param);
}
var asyncjobs = {};
for (var k = 0; k < params.length; k++)
{
if (params[k])
{
if (params[k].fileID && params[k].fileID.refvisit_tck)
{
derived_q.push(params[k])
continue;
}
else if (params[k].id.search("atlas") > -1)
{
if (params[k].project != undefined)
params[k].json = {
project: params[k].project
};
else
params[k].json = {
project: sharedLink.project
//static_info.atlas.project
};
params[k].intent.atlas=true;
params[k].fileID = params[k].fileID.replace("atlas_", "");
}
else
params[k].json = {
project: sharedLink.project
}
if (params[k].intent)
{
if (params[k].intent.isosurf != undefined)
{
asyncjobs["isosurf"] = true;
}
}
q.push(params[k]);
}
}
if (electron)
{
for (var k = 0; k < q.length;k++)
{
var filepath = q[k].fileID;
var overwritten = false;
if (openSharedLink.overrides != undefined )
{
if (openSharedLink.overrides[q[k].fileID] != undefined)
filepath = q[k].fileID
else if (openSharedLink.overrides[q[k].name] != undefined)
filepath = q[k].name
if (openSharedLink.overrides[filepath] != undefined)
{
console.log("override " + filepath + "=>" + openSharedLink.overrides[filepath])
filepath = openSharedLink.overrides[filepath]
openSharedLink.overrides[q[k].fileID] = filepath;
openSharedLink.overrides[q[k].name] = filepath;
overwritten=true;
}
}
if (!overwritten)
{
if (sharedLink.absolutePath)
filepath = path.join(sharedLink.absolutePath,filepath);
}
filepath = filepath.replace(/\\/g,"/"); // for windows
q[k].URLType = "localfile";
q[k].file = {name:filepath,local:true};
q[k].filename = filepath;
q[k].fileID = "localfile:"+filepath;
}
}
if (sharedLink.WorkstatePostCode != undefined && sharedLink.WorkstatePostCode.trim() != "")
{
KViewer.WorkstatePostCode = sharedLink.WorkstatePostCode
}
setTimeout(function(){
$(document.body).addClass("wait");
loadingQueue.execQueue(q, function() {
if (sharedLink.navi_movingObjs && sharedLink.navi_movingObjs.length > 0)
{
for (var k = 0; k < sharedLink.navi_movingObjs.length; k++)
KViewer.navigationTool.movingObjs[sharedLink.navi_movingObjs[k]] =
KViewer.dataManager.getFile(sharedLink.navi_movingObjs[k]);
KViewer.navigationTool.updateMoving();
}
if (sharedLink.navi_warp_enabled)
{
KViewer.navigationTool.transform.toggle();
}
if (sharedLink.mainviewport != undefined)
{
KViewer.currentPoint = math.matrix(sharedLink.position);
KViewer.toggleMainViewport(sharedLink.mainviewport, true);
}
else
KViewer.resetCrossHair( math.matrix(sharedLink.position));
if (sharedLink.tracking_params)
{
$.extend(KViewer.obj3dTool.tracking_panel.params,sharedLink.tracking_params);
KViewer.obj3dTool.tracking_panel.update();
}
if (sharedLink.ironSight)
{
ironSight.toggle();
}
if (sharedLink.WMQLpanels != undefined)
{
for (var k = 0; k < sharedLink.WMQLpanels.length;k++)
{
KWMQLPanel.openPanel(KViewer.obj3dTool.objs[sharedLink.WMQLpanels[k].tract_id],
KViewer.atlasTool.objs[sharedLink.WMQLpanels[k].atlas_id],
sharedLink.WMQLpanels[k]);
}
}
signalhandler.send("reslice positionChange");
for (var k=0;k < derived_q.length;k++)
{
if (derived_q[k].fileID.refvisit_tck != undefined)
{
var vport_id = derived_q[k].fileID.viewport_id;
var vmap_params = derived_q[k].fileID.refvisit_params;
var fibid = derived_q[k].fileID.refvisit_tck;
var medv = KViewer.viewports[vport_id].medViewer
if (medv == undefined)
continue;
var objs = KViewer.viewports[vport_id].medViewer.objects3D;
for (var j = 0; j < objs.length;j++)
if (KViewer.viewports[vport_id].medViewer.objects3D[j].fibers &&
KViewer.viewports[vport_id].medViewer.objects3D[j].fibers.fileID == fibid)
{
var tck = KViewer.viewports[vport_id].medViewer.objects3D[j];
var fobj;
if (vmap_params.terminal)
{
tck.visitworker_terms = tck.createVisitMap(vmap_params.undersamp,vmap_params.terminal,true,true);
fobj = tck.visitworker_terms.fobj;
}
else
{
tck.visitworker = tck.createVisitMap(vmap_params.undersamp,undefined,true,true);
fobj = tck.visitworker.fobj;
}
var target_vid = derived_q[k].intent.viewportID
KViewer.viewports[target_vid].setContent(fobj,{intent:derived_q[k].intent});
fobj;
}
}
}
/*
setTimeout(function()
{
for (var r = 0;r < markerProxy.markersets.length;r++)
{
for (var x in markerProxy.markersets[r].onupdate)
markerProxy.markersets[r].onupdate[x]();
}
},1000);
*/
if (sharedLink.TableHidden)
KViewer.toggleTableHide();
if (markerProxy != undefined && sharedLink.currentAnnot != undefined)
markerProxy.setCurrentSet(sharedLink.currentAnnot,true);
if (sharedLink.zoomedViewport != -1 && KViewer.viewports[sharedLink.zoomedViewport] != undefined )
KViewer.viewports[sharedLink.zoomedViewport].zoomViewPort();
$(document.body).removeClass("wait");
$("#KLoadingFrame").css('display','none')
if (KViewer.hasControlsOn() != sharedLink.controlsOn)
KViewer.toggleElementsForScreenShot();
/*
var ival = 100;
function waitForAsyncs()
{
if (asyncjobs['isosurf'] != undefined)
{
if (typeof BABYLON == "undefined" ||
BABYLON.initialized !== true ||
executeImageWorker.running_workers>0)
{
setTimeout(waitForAsyncs,ival)
return;
}
}
if (openSharedLink.callback != undefined)
openSharedLink.callback.execute();
}
setTimeout(waitForAsyncs,100)
*/
var post_fun = function() {}
if (openSharedLink.callback != undefined)
post_fun = openSharedLink.callback.execute;
if (sharedLink.WorkstatePostCode != undefined)
{
eval("var wdpc_fun = " +sharedLink.WorkstatePostCode);
wdpc_fun(post_fun)
}
else
post_fun();
if (onready)
onready();
});
},0);
}
}
function saveWorkstate(that)
{
var s = gatherState('savestate');
if (electron)
{
uploadJSON("workstate.json",s,{subfolder:'workstates',tag:'/workstate/'},function(){});
}
else
{
saveDialog("workstate", function(name,finfo)
{
finfo.tag = '/workstate/';
finfo.subfolder = "workstates";
that.lastProjectStatename = name;
uploadJSON(name,s,finfo,function(){});
} ,that.lastProjectStatename);
}
}
/** objectifies viewing state including all currently loaded files etc.
* @return {object} - an object containg all state information
*/
function gatherState(issuer)
{
function mapID(obj)
{
var id ;
if (obj.content && obj.content.refvisit_params)
{
id = {refvisit_tck:obj.content.refvisit_tck.fibers.fileID,
viewport_id:obj.content.refvisit_tck.viewer.viewport.viewPortID,
refvisit_params:obj.content.refvisit_params}
return id;
}
if (obj.fileID)
id = obj.fileID;
if (obj.currentFileID)
id = obj.currentFileID;
if (obj.trackingVolID)
id = obj.trackingVolID;
if (id == undefined && obj.atlas)
{
id =obj.atlas.fileID
}
if (id == undefined)
return undefined;
if (electron)
{
var file = KViewer.dataManager.getFile(id);
return file.fileinfo.SubFolder + "/" + file.fileinfo.filename;
}
else
{
return id;
}
}
// gather viewport content and overlays
var imgs = [];
for (var k = 0; k < KViewer.viewports.length; k++)
{
if (KViewer.viewports[k] == undefined)
continue;
if(KViewer.viewports[k].getCurrentViewer)
var viewer = KViewer.viewports[k].getCurrentViewer();
else
viewer = undefined;
var viewportProperties = undefined;
if (KViewer.viewports[k].getProperties)
viewportProperties = KViewer.viewports[k].getProperties();
if (viewer != undefined
&& ( ( viewer.currentFileID != undefined & viewer.currentFileID != "") || (viewer.nii != undefined && viewer.nii.dummy != undefined) ) )
{
var myid = "ID" + viewer.currentFileID;
if (viewer.currentFileID=="WorkstatePostCode")
continue;
if (viewer.viewerType == "medViewer")
{
var gl_props;
if (viewer.isGLenabled() && viewer.gl != undefined)
gl_props = {alpha:viewer.gl.camera.alpha,
beta:viewer.gl.camera.beta,
radius:viewer.gl.camera.radius,
target:viewer.gl.camera.target,
position:viewer.gl.camera.position,
planesVisibility:viewer.gl.getPlanesVisibility()};
if (viewer.currentFileID != undefined)
{
var main_img = {
id: myid,
fileID: mapID(viewer), //viewer.currentFileID,
URLType: 'serverfile',
name:viewer.currentFilename,
intent:
{
viewportID: k,
viewportProperties:viewportProperties,
zooms: viewer.getRelativeZoomLims(),
cmap: viewer.histoManager.cmapindex,
windowing: viewer.histoManager.getManuallyEnteredClim(issuer == 'savestate'),
gl: viewer.isGLenabled(),
gl_props:gl_props,
isosurf: (viewer.refSurfView!=undefined)?viewer.refSurfView.getViewProperties():undefined,
slicing: viewer.getSlicingDimOfWorld(),
showcolored_type:viewer.showcolored_type,
showcolored:viewer.showcolored,
transfactor:viewer.transfactor
}
};
if (viewer.mosaicview && viewer.mosaicview.active)
{
main_img.mosaic = {border:viewer.mosaicview.border,
nx: viewer.mosaicview.nx,
nx_cont: viewer.mosaicview.nx_cont,
zoom: viewer.mosaicview.zoom,
start:viewer.mosaicview.start,
end:viewer.mosaicview.end,
clipratio:viewer.mosaicview.clipratio,
number_color:viewer.mosaicview.number_color
};
}
if (viewer.nii && viewer.nii.quiver_params)
{
main_img.intent.quiver_params = viewer.nii.quiver_params;
}
imgs.push(main_img);
}
if (viewer.overlays != undefined)
for (var j = 0; j < viewer.overlays.length; j++)
{
var myid = "ID" + viewer.overlays[j].currentFileID;
imgs.push({
id: myid + "ovl",
fileID: mapID(viewer.overlays[j]),
name:viewer.overlays[j].currentFilename,
URLType: 'serverfile',
intent:
{
overlay: true,
viewportID: k,
isosurf: (viewer.overlays[j].refSurfView!=undefined)?viewer.overlays[j].refSurfView.getViewProperties():undefined,
cmap: viewer.overlays[j].histoManager.cmapindex,
transparent: viewer.overlays[j].histoManager.blending,
windowing: viewer.overlays[j].histoManager.getManuallyEnteredClim(issuer=='savestate'),
showcolored:viewer.overlays[j].showcolored,
showcolored_type:viewer.overlays[j].showcolored_type,
posnegsym:viewer.overlays[j].histoManager.posnegsym,
blocky:viewer.overlays[j].histoManager.blocky,
visible:viewer.overlays[j].visible,
hideview:viewer.overlays[j].hideview,
quiver_params:viewer.overlays[j].nii.quiver_params,
outlines: (viewer.overlays[j].outlines != undefined)?((viewer.overlays[j].color != undefined)?viewer.overlays[j].color:0):undefined
}
});
}
if (viewer.ROIs != undefined)
for (var j = 0; j < vi