UNPKG

@galaxyproject/nora

Version:

NORA Medical Imaging Viewer

1,479 lines (1,210 loc) 353 kB
/** * @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