function add_annotations(){ //this may not be needed if the external script loads before document scripts are evaluated function onjq(){ if(typeof(jQuery) === "undefined" ){ window.setTimeout(onjq,100); // console.log("$ not defined") return; } setupAnnotations(); } //wait for jQuery to be loaded; try at the end of the current event loop, then every 100ms window.setTimeout(onjq,0); function setupAnnotations(){ addControls(); annotationInit(); prepCanvasForAnnotations(); slideviewer.addHandler("update-viewport",redrawAnnotations); } var slideviewer = window.slideviewer; //read from global var info = window.slideinfo; //read from global function getMpp(){return info.mpp;} var paint; //Flag for whether an annotation is in progress var annotationType = ""; //Indicates which type of annotation (e.g. arrow, rectangle) is due to be drawn next or in progress var imgWidth; //Width of the viewer var imgHeight; //Height of the viewer var imgAspectRatio; //Aspect ratio of the viewer var contextZoom; //Current zoom level (for use when actually drawing) var annotationsDefined = false; var inactiveButtonColor = "#DCDCDC"; var activeButtonColor = "#FF4500" //var rulerWidth = 25; var rulerWidth = 5; var fontSize = 20; //Freehand annotations var freehandPt = new Array(); var clickDrag = new Array(); //Arrow annotations var arrowStart = new Array(); var arrowEnd = new Array(); //Rectangle annotations var rectStart = new Array(); var rectEnd = new Array(); //Circle annotations var circleEdge1 = new Array(); var circleEdge2 = new Array(); //Text annotations var textAnnotation = new Array(); var textLocation = new Array(); //Ruler annotations var rulerStart = new Array(); var rulerEnd = new Array(); function annotationInit() { //perform one-time initialization when viewer first loads $("#colorAnnotation").simpleColor({ boxWidth:20, boxHeight:20, }); } function prepCanvasForAnnotations() { //Initialization for each slide imgWidth = slideviewer.source.dimensions.x; imgHeight = slideviewer.source.dimensions.y; imgAspectRatio = imgWidth / imgHeight; var overlayCanvas = slideviewer.drawer.canvas; overlayCanvas.addEventListener("mousedown", doMouseDown, false); overlayCanvas.addEventListener("mousemove", doMouseMove, false); overlayCanvas.addEventListener("mouseup", doMouseUp, false); } function addAnnotation(type) { if (slideviewer.isMouseNavEnabled()) { slideviewer.setMouseNavEnabled(false); annotationType = type; $("#"+type+"Annotation").css("background-color", activeButtonColor); } else { //Clicking on another annotation button before drawing an annotation if (type === annotationType) { //Same button; turn annotation mode off annotationModeOff(); } else { //switch annotation types $("#"+annotationType+"Annotation").css("background-color", inactiveButtonColor); annotationType = type; $("#"+type+"Annotation").css("background-color", activeButtonColor); } } } function annotationModeOff() { paint = false; slideviewer.setMouseNavEnabled(true); $("#"+annotationType+"Annotation").css("background-color", inactiveButtonColor); annotationType = ""; } function offsetX(x) { //x is in pixels from the edge of the window //account for the offset of the viewport (e.g. edge of the visible image) from the edge of the window var mouseX = x - $("#view").offset().left; //Get the percentage ofdistance across the screen represented by x var screenPortion = mouseX/slideviewer.container.clientWidth; //Viewport width is the percentage of the total image visible in the viewport //screenPortion is the mouse x position as a percentage across the viewport //Get the number of pixels (in image space) from the edge of the visible portion of the image to the position represented by x var imagePortion = screenPortion * slideviewer.viewport.getBounds(true).width * imgWidth; //Get the number of pixels (in image space) from the beginning of the image to the edge of the visible portion of the image // plus the number of pixels from the edge of the visible portion to the mouse x position return (slideviewer.viewport.getBounds(true).x*imgWidth) + imagePortion; } function offsetY(y) { var mouseY = y - $("#view").position().top; var screenPortion = mouseY/slideviewer.container.clientHeight; var imagePortion = screenPortion * slideviewer.viewport.getBounds(true).height * imgHeight * imgAspectRatio; return (slideviewer.viewport.getBounds(true).y*imgHeight*imgAspectRatio) + imagePortion; } function doMouseDown(event){ if (event.which == 1) { if ((annotationType != "") && !slideviewer.isMouseNavEnabled()) { paint = true; if (annotationType === "freehand") { addClick(offsetX(event.pageX), offsetY(event.pageY), false); annotationsDefined = true; } else if (annotationType === "arrow") { var newArrow = new OpenSeadragon.Point(offsetX(event.pageX), offsetY(event.pageY)); arrowStart.push(newArrow); arrowEnd.push(newArrow); annotationsDefined = true; } else if (annotationType === "ruler") { var newRuler = new OpenSeadragon.Point(offsetX(event.pageX), offsetY(event.pageY)); rulerStart.push(newRuler); rulerEnd.push(newRuler); annotationsDefined = true; } else if (annotationType === "rectangle") { var rectOrigin = new OpenSeadragon.Point(offsetX(event.pageX), offsetY(event.pageY)); rectStart.push(rectOrigin); rectEnd.push(rectOrigin); annotationsDefined = true; } else if (annotationType === "circle") { var center = new OpenSeadragon.Point(offsetX(event.pageX), offsetY(event.pageY)); circleEdge1.push(center); circleEdge2.push(center); annotationsDefined = true; } redrawAnnotations(); } } } function doMouseMove(event) { if (event.which == 1) { if ((annotationType != "") && !slideviewer.isMouseNavEnabled()) { if (paint){ if (annotationType === "freehand") { addClick(offsetX(event.pageX), offsetY(event.pageY), true); } else if (annotationType === "arrow") { arrowEnd[arrowEnd.length - 1] = new OpenSeadragon.Point(offsetX(event.pageX), offsetY(event.pageY)); } else if (annotationType === "ruler") { rulerEnd[rulerEnd.length - 1] = new OpenSeadragon.Point(offsetX(event.pageX), offsetY(event.pageY)); } else if (annotationType === "rectangle") { rectEnd[rectEnd.length - 1] = new OpenSeadragon.Point(offsetX(event.pageX), offsetY(event.pageY)); } else if (annotationType === "circle") { circleEdge2[circleEdge2.length - 1] = new OpenSeadragon.Point(offsetX(event.pageX), offsetY(event.pageY)); } redrawAnnotations(); } } } } function doMouseUp(event) { if (event.which == 1) { if (annotationType === "text") { var newLabel = prompt("Enter text for a label to add to the slide"); var labelLocation = new OpenSeadragon.Point(offsetX(event.pageX), offsetY(event.pageY)); textAnnotation.push(newLabel); textLocation.push(labelLocation); annotationsDefined = true; } if ((annotationType != "") && !slideviewer.isMouseNavEnabled()) { if (paint) { annotationModeOff(); redrawAnnotations(); } } } } function addClick(x, y, dragging) { freehandPt.push(new OpenSeadragon.Point(x,y)); clickDrag.push(dragging); } function redrawAnnotations() { if (annotationsDefined) { var viewportOrigin = new OpenSeadragon.Point(0, 0); var boundsRect = slideviewer.viewport.getBounds(true); viewportOrigin.x = boundsRect.x; viewportOrigin.y = boundsRect.y * imgAspectRatio; var viewportWidth = boundsRect.width; var viewportHeight = boundsRect.height * imgAspectRatio; var viewportZoom = slideviewer.viewport.getZoom(true); var image1 = slideviewer.world.getItemAt(0); var zoom = image1.viewportToImageZoom(viewportZoom); var offsetX=(((viewportOrigin.x/imgWidth)-viewportOrigin.x)/viewportWidth)*slideviewer.container.clientWidth; var offsetY=(((viewportOrigin.y/imgHeight)-viewportOrigin.y)/viewportHeight)*slideviewer.container.clientHeight; //Translate, zoom, and setTransform functions don"t appear to be working properly in IE11. slideviewer.drawer.context.translate(offsetX,offsetY); slideviewer.drawer.context.scale(zoom,zoom); drawAllIndividualAnnotations(offsetX, offsetY, zoom); slideviewer.drawer.context.setTransform(1, 0, 0, 1, 0, 0); slideviewer.forceRedraw(); } } function transform(coordinate, offset) { //return ((coordinate*contextZoom)+offset)*contextMultiplier; return ((coordinate*contextZoom)+offset); } function drawAllIndividualAnnotations(offsetX, offsetY, zoom){ contextZoom = zoom; //used by transform() //Get the drawing context var context = slideviewer.drawer.context; //Set the drawing style, thickness, color context.lineJoin = "round"; context.lineWidth = 5/zoom; context.strokeStyle = $("#colorAnnotation").val(); //Begin a path which will include all arrows, rectangles, freehand shapes, and text labels (not circles or rulers) context.beginPath(); for(var i=0; i < freehandPt.length; i++) { //For each freehand point: if ((i === 0) || !clickDrag[i]){ //If this is the start of the first shape, or a new shape, indicate where to start drawing context.moveTo(freehandPt[i].x-1, freehandPt[i].y); } else { //Draw a line to the new point context.lineTo(freehandPt[i].x, freehandPt[i].y); } } for (var i=0; i < arrowStart.length; i++) { var toX = arrowStart[i].x; var toY = arrowStart[i].y; var fromX = arrowEnd[i].x; var fromY = arrowEnd[i].y; var dx = fromX-toX; var dy = fromY-toY; var d = Math.sqrt(dx*dx+dy*dy); var headlen = (Math.min(Math.max(10, d/5),25))/zoom; var angle = Math.atan2(toY-fromY,toX-fromX); context.moveTo(fromX, fromY); context.lineTo(toX, toY); context.lineTo(toX-headlen*Math.cos(angle-Math.PI/6),toY-headlen*Math.sin(angle-Math.PI/6)); context.moveTo(toX,toY); context.lineTo(toX-headlen*Math.cos(angle+Math.PI/6),toY-headlen*Math.sin(angle+Math.PI/6)); } for (var i=0; i < rectStart.length; i++) { var startX = rectStart[i].x; var startY = rectStart[i].y; var endX = rectEnd[i].x; var endY = rectEnd[i].y; context.rect(startX, startY, endX-startX, endY-startY); } if (textAnnotation.length > 0) { var lblFont = fontSize/zoom; context.font=lblFont+"px Georgia"; context.fillStyle = context.strokeStyle; for (var i=0; i 0) { context.translate(rulerStart[i].x, rulerStart[i].y); context.rotate(Math.acos(dx/d)); } else { context.translate(rulerEnd[i].x, rulerEnd[i].y); context.rotate(Math.acos(dx/d)+Math.PI); } //Draw bar context.fillStyle = "#FFD700"; var rulerHeight = rulerWidth/zoom; context.fillRect(0,0,d, rulerHeight); //Draw tick marks every 100 microns //Translate the total distance from pixels (in whole slide coordinates) to microns var microns = pixels2microns(dx, dy); //Get the start position of the line var startPosition = Math.min(d,0); //Change drawing color to black context.fillStyle = "#000000"; //Get the number of pixels in 100 microns var tickThickness = 2/zoom; var tickDistance = Math.abs((d/microns)*100); var ticksBetweenMm = 10; if (tickDistance < (tickThickness*5)) { //Switch from ticks every 100 microns to ticks every 500 microns, i.e. 0.5 mm tickDistance = tickDistance * 5; ticksBetweenMm = 2; } if (tickDistance < (tickThickness*5)) { //Switch from ticks every half mm to ticks every mm tickDistance = tickDistance * 2; ticksBetweenMm = 1; } //Draw tick marks var tickCount = 0; for (var inc=tickDistance; inc",{"style":"display:inline-block;"}).appendTo(parentContainer); $("",{id:"arrowAnnotation", type:"image",class: "annotationButton", title:"Draw arrow annotation" }).appendTo(c).on("click",function(){addAnnotation("arrow"); }).attr("src","data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAABNklEQVRIS2NkoDFgpLH5DKMWEAzhAQmi/82NjQy19fVUsRzFECcnp//79u0De5talqBYUJCf/3/2nDkMX79+BVsiLCzMUJCXR5FvMIKhubHx/+07dxgWLV4Mj0B5eXmGlKQksizCGc4gi2rr6xlkZGQYnjx5QnawEYxImEXI6VFKSorh2bNnML3/GRhwZ1iCFsAM9vfz+795yxaGf//+gYXMzc0ZTp48CdL/X1NTk+H69etYzSLaAmjK+r923TqGCxcvYmQwXKmOJAuQTAUFCwaoqa5maGltRTGTXAtghmNY1NfTw1BUUgI3l1wL/gf4+zNcuHCB4fmLFww/f/6E+4aVlZXh9+/flFkASlno4fPg4UOGS5cuMdx/8IDhy5cvDD9+/ABbQq4PCJaiMAWjFhAMqqEfRAC+sG4Z0y9+bgAAAABJRU5ErkJggg=="); $("",{id:"rectangleAnnotation", type:"image",class: "annotationButton", title:"Draw rectangle annotation" }).appendTo(c).on("click",function(){addAnnotation("rectangle"); }).attr("src","data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAhklEQVRIS2NkoDFgpLH5DKMWEAxh+gXRtq1b/3t5ezOIiooyyMvLE3QZuoJ/f/8ynDt/nsHS0pLh+PHjcIfDGTALoqOiGJYuW0auz/6PWkAobkaDiFAIMYwG0WAOIpDbmJiY/v/794+gM/EpyEhPZ5gxcyZmWUSRqXg0k1uoEe2eUQsIBhUAROyNGTrxWNIAAAAASUVORK5CYII="); $("",{id:"circleAnnotation", type:"image",class: "annotationButton", title:"Draw cirlce annotation" }).appendTo(c).on("click",function(){addAnnotation("circle"); }).attr("src","data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAl0lEQVRIS+1VSQ7AIAjU/z+6jSYkiIwwaeil7bXMJoi9FX+9mL9lBS5gJMRHBYjY6kEe9EMTMya2Wg/MkEsSiDkJRM7tMYnIgrMkbhExaRveE2Cda/2jwFP3th/TqHb7C3zgiEbE0eiyMRUBO13EPZsGF/zrq0KnYJLAO5RZ1yehcPNGDS17cNAqTr9kUhglYCbIrS0XuAGIyCQZQ5GmtQAAAABJRU5ErkJggg=="); $("",{id:"textAnnotation", type:"image",class: "annotationButton", title:"Draw text annotation" }).appendTo(c).on("click",function(){addAnnotation("text"); }).attr("src","data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAADRklEQVRIS62VS0hbURCG/1TEhS5Eq3ERiEIlEapEb1CR7JJFXVWIrtxZXxVFlFCsRMUHasFNUaxB0YXFTdViV5aqiCh0YYj4zK1VU0htEgUFI2KDnjIHcjHGaKxeCEk498w3/8yc/8jwwMfj+cPi4xNDRpE9MD4IkJCQhKKiIuTk5MBkMqG8vBwejxuTk59xK6C6uor19fUH5VBdXYW+vn6+1w+4vLzE6ekpLBYL6uvrpT0hAU7nLyYI2TAYDDg/P4fP9xde7ymOj4+xt7eH1VUbFAol3+92/2Yy2RNcXAAajQb7+/s4PHRxSEjAxMQn1tnZDbVaDa/3hAcn0NnZGQcMDlpgNBZJ+0nJVUBERARfCwkoLCxkfp1HR0ew2WxISUmB1+vlKnJzczA19eX/AA7HT5aVlQ2j0Yjd3V0e0Gq1QqVScSb9p48obiA5+ZnUi7AVWCz9bHR0DElJSXC5XNje3obP5+PBIyMjpd9dXR2oqKi6P0Cn0zGlUsmDU6br6+sYGPiAysrXiI6O5tNC/dDpdFhcXLwfYG3NxgyGF3x67HY7nE4nYmNjIYqiTKVSMYfDwYPTExUVheXl70hPz5SF3eT29lY2P7+AmJho2O0iDg4OUFdXi6amFhmtdXR0BgDM5ka+FjaAstRqtVL2VCJ/lqROq82VAKSCGk/qwgIsLS2wkpIyEGBmZoY3k0bTarVK4ygIAqOJ8j9Uprm5b0hNTQ04aDeeA7KGrS2RZ7i5ucljtLa2oKamVgL09r5nJtObgDKVlb1Cc3PT3QC5XM6ouZQ9lcbfzLsMMS4uDnb7xu0AsobGRjPUahWmp7/y4IIgBFgFQekki6IYwKQyjY19RF6eTvKioBKRNZAl0FheD3CXAlovLi5GT0/PzQByzoyMTOTn52N8fDzs0lwFk4qdnR8gByY3DVAwMjLMurvfQaFQYHZ2lu+Ty+UoLS0JmfzQ0DDcbnfANNFpb2h4GwzQ6/WMAlMWVHv69h+gUITrh47e0+v13FYCFGxtrTONRgiKs7JiRVra85B2fn2fPzGylaAShdPEUO/4r0xaPzk54Vcm3cv00DX6aJd+QcFLaLUCzOZmfuknJj5FW1vH4wFuUkgK/gGz7RexjNT2pgAAAABJRU5ErkJggg=="); $("",{id:"rulerAnnotation", type:"image",class: "annotationButton", title:"Draw ruler annotation" }).appendTo(c).on("click",function(){addAnnotation("ruler"); }).attr("src","data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAABbklEQVRIS9VVwY3CMBDcNJE0ga+J8EJUEF5AE6QC7kVoAHiRCiJeoQlME3YTPo2FfcY2CbqAxPmBzHp3x7M76yT05pW8OT99DsBhv1fzxSJ6oe/1WqESq7IMzo1BO3jLOo/zXJ+353OQAGdCSsrSlPj1SofdjibTqfWzAEr9YiSJNusfxphCcAzg1DRqvlySEEL7zopCZVlGm6qKA9wSGyLaCUkunFPbtgEDlMa1+//tLYlIPWIAp1hgzN4LEGPwUgBbG6cHXQA3f3VqGt1Y9GtWFHdqumvyswzcUmC/KkuNNc7zoE+9Kho66U8xGALSyQASxRCZhUnGRAshbJ1Roi/GCFLGJBtZw4a+dDJAcLXdUpqmJKUkNhoRALFHvY91jXjFGAtIwgcD2MkAAMe6Js554u+dAbMqAphRFPZ4DXoZmES+clwA86yYpEa+AYA/B4+SehOrb+omjQLEXtNXAgxRYmfs53zR/krx/zP4AYBwWii33RBMAAAAAElFTkSuQmCC"); $("",{id:"freehandAnnotation", type:"image",class: "annotationButton", title:"Draw freehand annotation" }).appendTo(c).on("click",function(){addAnnotation("freehand"); }).attr("src","data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAACOklEQVRIS82Wv2taURTHv1FRXPQFU1KI8MRJEHES0sUHbm7pUJJULZoOaQ0NNENtDSSkU7PEahJJ0qFBA+ogzV8g6FJXN2lAfPCGBio+dNDtlCdJqPr8FRF613vO93PPueece+cw4zU3Y338P4AnCwskRatnGLze2MCncHisw400ikWjtLe/D1EUu7J5lUzC6/ON9B9q8PngoCN+tzq2V4kEHZ+c4NfNDer1+nSA41iM3m1vg2VZ8DzfJWYwGKhWq0nMoZCBm98uLii8u4tms4nI0RHeBoNdtvMMQ9KliKL4OAAA0uv1OIvHse7xyIkQx3HI5/OTA2w2GzUajb609PQMfQyF8OXwcHLA85UV0mg0SGcyA511Op10iKkumaTcv9/ZkRVRKBSUSaXwYnV18gikVEgCarUa7XZ7kADZ7XaUSqXHAcxmM1UqFbzZ3MTZ+XmfiFarpVarhWfLy/hZLA6EyG4E/H6SOvfH9fXIHDudTioUCgj4/fh+edlnLytgsVioXC6PFL+vqvjpKQW3tiBXtrIiUhPVRzRQ75hnWZZ4nscrnw+JZPJBVxbgdrvJ5/Hgpdc7dhR3QDIajRAEYTjg3nieYRCJROAPBMYCuVwuyuVyXfNpoCPHcZTP56FUKuFwOFAcUin/pIueLi7i9+3tWBFIQ46isRiq1eqDhkqlglT/xqUlWK1WmEymzt6HUKjzZvSOj7FC/xqJUDabxZ9arTNdBUGQfcrX19aQSqe7NMcCTPMxmDngL4sWzhlvDa3oAAAAAElFTkSuQmCC"); $("",{id:"colorAnnotation", class:"annotationButton", value:"#009900", title:"Change annotation color"}).on("change",redrawAnnotations).insertAfter(c); } }