diff --git a/examples/volume-viewer-demo.html b/examples/volume-viewer-demo.html index 69b4c91d..d7f1f273 100644 --- a/examples/volume-viewer-demo.html +++ b/examples/volume-viewer-demo.html @@ -111,6 +111,9 @@ Transverse + Annotations: + + diff --git a/src/brainbrowser/volume-viewer.js b/src/brainbrowser/volume-viewer.js index decb2028..8d6fa468 100644 --- a/src/brainbrowser/volume-viewer.js +++ b/src/brainbrowser/volume-viewer.js @@ -479,9 +479,8 @@ // Space if (key === 32) { - event.preventDefault(); - if (volume.header.time) { + event.preventDefault(); if (event.shiftKey) { time = Math.max(0, volume.current_time - 1); } else { diff --git a/src/brainbrowser/volume-viewer/modules/annotate.js b/src/brainbrowser/volume-viewer/modules/annotate.js new file mode 100644 index 00000000..d6321ac0 --- /dev/null +++ b/src/brainbrowser/volume-viewer/modules/annotate.js @@ -0,0 +1,176 @@ +/* +* BrainBrowser: Web-based Neurological Visualization Tools +* (https://brainbrowser.cbrain.mcgill.ca) +* +* Copyright (C) 2011 +* The Royal Institution for the Advancement of Learning +* McGill University +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +*/ + +/* + * Author: Robert D. Vincent + */ + +BrainBrowser.VolumeViewer.modules.annotate = function(viewerP) { + "use strict"; + var viewer = viewerP; + + console.log("Annotation module is loaded."); + + // Add keyboard controls + addKeyboardControls(); + + // We use this for triggering downloads. + var elem = document.createElement('a'); + elem.id = 'anno-download'; + elem.style.display = 'none'; + document.body.appendChild(elem); + + function triggerUpdate(volume) { + volume.display.forEach(function(panel) { + panel.updateSlice(); + }); + } + + function closeAnnotation() { + console.log('close'); + var elem = document.getElementById('anno-box'); + if (elem) { + document.body.removeChild(elem); + } + } + + function saveAnnotation(text) { + if (!viewer.active_panel) + return; + var panel = viewer.active_panel; + var volume = panel.volume; + var annotation = { + position: volume.getWorldCoords(), + content: text + }; + console.log('save'); + if (typeof volume.annotations === "undefined") { + volume.annotations = [] + } + volume.annotations.push(annotation); + + var select = document.getElementById('anno-list-' + panel.volume_id); + var option = document.createElement('option'); + option.text = text; + option.value = panel.volume_id + ':' + (volume.annotations.length - 1); + select.add(option); + } + + function makeAnnotation() { + if (!viewer.active_panel) + return; + var panel = viewer.active_panel; + var canvas = panel.canvas; + var cursor = panel.getCursorPosition(); + var newbox = document.createElement('div'); + var x = cursor.x + canvas.offsetLeft; + var y = cursor.y + canvas.offsetTop; + + newbox.style.backgroundColor = 'beige'; + newbox.style.border = 'thin solid gray'; + newbox.id = "anno-box"; + newbox.style.position = "absolute"; + newbox.style.top = y + 'px'; + newbox.style.left = x + 'px'; + + var textarea = document.createElement('textarea'); + textarea.id = 'anno-body'; + //textarea.value = "x: " + x + " y: " + y; + textarea.style.width = '180pt'; + textarea.style.display = 'block'; + newbox.appendChild(textarea); + + document.body.appendChild(newbox); + + var elem = document.createElement('a'); + elem.setAttribute('href', '#save'); + elem.id = 'anno-save'; + elem.style.display = 'inline-block'; + elem.style.width = (newbox.offsetWidth / 2) + 'px'; + elem.innerHTML = 'Save'; + newbox.appendChild(elem); + + elem = document.createElement('a'); + elem.setAttribute('href', '#cancel'); + elem.id = 'anno-cancel'; + elem.style.display = 'inline-block'; + elem.style.width = (newbox.offsetWidth / 2) + 'px'; + elem.innerHTML = 'Cancel'; + newbox.appendChild(elem); + + // Save the note. + $('#anno-save').click(function() { + saveAnnotation(textarea.value); + closeAnnotation(); + }); + + // Just close the note. + $('#anno-cancel').click(function() { + closeAnnotation(); + }); + + $('#anno-list-' + panel.volume_id).click(function(e) { + console.log('click: ' + e.target.value); + var array = e.target.value.split(':'); + var volume_id = parseInt(array[0]); + var n_anno = parseInt(array[1]); + var volume = viewer.volumes[volume_id]; + var anno = volume.annotations[n_anno]; + volume.setWorldCoords(anno.position.x, anno.position.y, anno.position.z); + triggerUpdate(volume); + }); + + $('#anno-list').change(function(e) { + console.log('change: ' + e.target.value); + }); + } + + function addKeyboardControls() { + document.addEventListener("keydown", function(event) { + var key = event.which; + + var keys = { + 65: function() { + makeAnnotation(); + }, + 66: function() { + if (!viewer.active_panel) + return; + var panel = viewer.active_panel; + var volume = panel.volume; + var anno_str = JSON.stringify(volume.annotations, null, 2); + var data = "data:text/json;charset=utf-8," + + encodeURIComponent(anno_str); + var elem = document.getElementById('anno-download'); + elem.setAttribute("href", data); + elem.setAttribute("download", "annotation.json"); + elem.click(); + } + }; + + if (typeof keys[key] === "function" && event.ctrlKey) { + event.preventDefault(); + keys[key](); + } + }); + } +}