4228 lines
337 KiB
Plaintext
4228 lines
337 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"%matplotlib notebook\n",
|
|
"\n",
|
|
"import numpy as np\n",
|
|
"import toolz\n",
|
|
"import matplotlib.pyplot as plt"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Frequency response\n",
|
|
"\n",
|
|
"Implicitly plot the function\n",
|
|
"\n",
|
|
"\\begin{align}\n",
|
|
"\\frac{\\gamma^2}{z^2} &= (\\omega^2 - \\alpha - \\frac{3}{4}\\beta z^2)^2 + (\\delta \\omega)^2 \\implies \\\\\n",
|
|
"z &= \\frac{\\gamma}{\\sqrt{(\\omega^2 - \\alpha - \\frac{3}{4}\\beta z^2)^2 + (\\delta \\omega)^2}} \\implies\n",
|
|
"\\end{align}\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def right(gamma, alpha, beta, delta, z, omega):\n",
|
|
" denom = (omega**2 - alpha - (3/4)*beta*z**2)**2 + (delta*omega)**2\n",
|
|
" return gamma/np.sqrt(denom)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"application/javascript": [
|
|
"/* Put everything inside the global mpl namespace */\n",
|
|
"window.mpl = {};\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.get_websocket_type = function() {\n",
|
|
" if (typeof(WebSocket) !== 'undefined') {\n",
|
|
" return WebSocket;\n",
|
|
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
|
|
" return MozWebSocket;\n",
|
|
" } else {\n",
|
|
" alert('Your browser does not have WebSocket support.' +\n",
|
|
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
|
|
" 'Firefox 4 and 5 are also supported but you ' +\n",
|
|
" 'have to enable WebSockets in about:config.');\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
|
|
" this.id = figure_id;\n",
|
|
"\n",
|
|
" this.ws = websocket;\n",
|
|
"\n",
|
|
" this.supports_binary = (this.ws.binaryType != undefined);\n",
|
|
"\n",
|
|
" if (!this.supports_binary) {\n",
|
|
" var warnings = document.getElementById(\"mpl-warnings\");\n",
|
|
" if (warnings) {\n",
|
|
" warnings.style.display = 'block';\n",
|
|
" warnings.textContent = (\n",
|
|
" \"This browser does not support binary websocket messages. \" +\n",
|
|
" \"Performance may be slow.\");\n",
|
|
" }\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.imageObj = new Image();\n",
|
|
"\n",
|
|
" this.context = undefined;\n",
|
|
" this.message = undefined;\n",
|
|
" this.canvas = undefined;\n",
|
|
" this.rubberband_canvas = undefined;\n",
|
|
" this.rubberband_context = undefined;\n",
|
|
" this.format_dropdown = undefined;\n",
|
|
"\n",
|
|
" this.image_mode = 'full';\n",
|
|
"\n",
|
|
" this.root = $('<div/>');\n",
|
|
" this._root_extra_style(this.root)\n",
|
|
" this.root.attr('style', 'display: inline-block');\n",
|
|
"\n",
|
|
" $(parent_element).append(this.root);\n",
|
|
"\n",
|
|
" this._init_header(this);\n",
|
|
" this._init_canvas(this);\n",
|
|
" this._init_toolbar(this);\n",
|
|
"\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" this.waiting = false;\n",
|
|
"\n",
|
|
" this.ws.onopen = function () {\n",
|
|
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
|
|
" fig.send_message(\"send_image_mode\", {});\n",
|
|
" if (mpl.ratio != 1) {\n",
|
|
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
|
|
" }\n",
|
|
" fig.send_message(\"refresh\", {});\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.imageObj.onload = function() {\n",
|
|
" if (fig.image_mode == 'full') {\n",
|
|
" // Full images could contain transparency (where diff images\n",
|
|
" // almost always do), so we need to clear the canvas so that\n",
|
|
" // there is no ghosting.\n",
|
|
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
|
|
" }\n",
|
|
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
|
|
" };\n",
|
|
"\n",
|
|
" this.imageObj.onunload = function() {\n",
|
|
" fig.ws.close();\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.ws.onmessage = this._make_on_message_function(this);\n",
|
|
"\n",
|
|
" this.ondownload = ondownload;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_header = function() {\n",
|
|
" var titlebar = $(\n",
|
|
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
|
|
" 'ui-helper-clearfix\"/>');\n",
|
|
" var titletext = $(\n",
|
|
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
|
|
" 'text-align: center; padding: 3px;\"/>');\n",
|
|
" titlebar.append(titletext)\n",
|
|
" this.root.append(titlebar);\n",
|
|
" this.header = titletext[0];\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_canvas = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var canvas_div = $('<div/>');\n",
|
|
"\n",
|
|
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
|
|
"\n",
|
|
" function canvas_keyboard_event(event) {\n",
|
|
" return fig.key_event(event, event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
|
|
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
|
|
" this.canvas_div = canvas_div\n",
|
|
" this._canvas_extra_style(canvas_div)\n",
|
|
" this.root.append(canvas_div);\n",
|
|
"\n",
|
|
" var canvas = $('<canvas/>');\n",
|
|
" canvas.addClass('mpl-canvas');\n",
|
|
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
|
|
"\n",
|
|
" this.canvas = canvas[0];\n",
|
|
" this.context = canvas[0].getContext(\"2d\");\n",
|
|
"\n",
|
|
" var backingStore = this.context.backingStorePixelRatio ||\n",
|
|
"\tthis.context.webkitBackingStorePixelRatio ||\n",
|
|
"\tthis.context.mozBackingStorePixelRatio ||\n",
|
|
"\tthis.context.msBackingStorePixelRatio ||\n",
|
|
"\tthis.context.oBackingStorePixelRatio ||\n",
|
|
"\tthis.context.backingStorePixelRatio || 1;\n",
|
|
"\n",
|
|
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
|
|
"\n",
|
|
" var rubberband = $('<canvas/>');\n",
|
|
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
|
|
"\n",
|
|
" var pass_mouse_events = true;\n",
|
|
"\n",
|
|
" canvas_div.resizable({\n",
|
|
" start: function(event, ui) {\n",
|
|
" pass_mouse_events = false;\n",
|
|
" },\n",
|
|
" resize: function(event, ui) {\n",
|
|
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
|
" },\n",
|
|
" stop: function(event, ui) {\n",
|
|
" pass_mouse_events = true;\n",
|
|
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
|
" },\n",
|
|
" });\n",
|
|
"\n",
|
|
" function mouse_event_fn(event) {\n",
|
|
" if (pass_mouse_events)\n",
|
|
" return fig.mouse_event(event, event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" rubberband.mousedown('button_press', mouse_event_fn);\n",
|
|
" rubberband.mouseup('button_release', mouse_event_fn);\n",
|
|
" // Throttle sequential mouse events to 1 every 20ms.\n",
|
|
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
|
|
"\n",
|
|
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
|
|
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
|
|
"\n",
|
|
" canvas_div.on(\"wheel\", function (event) {\n",
|
|
" event = event.originalEvent;\n",
|
|
" event['data'] = 'scroll'\n",
|
|
" if (event.deltaY < 0) {\n",
|
|
" event.step = 1;\n",
|
|
" } else {\n",
|
|
" event.step = -1;\n",
|
|
" }\n",
|
|
" mouse_event_fn(event);\n",
|
|
" });\n",
|
|
"\n",
|
|
" canvas_div.append(canvas);\n",
|
|
" canvas_div.append(rubberband);\n",
|
|
"\n",
|
|
" this.rubberband = rubberband;\n",
|
|
" this.rubberband_canvas = rubberband[0];\n",
|
|
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
|
|
" this.rubberband_context.strokeStyle = \"#000000\";\n",
|
|
"\n",
|
|
" this._resize_canvas = function(width, height) {\n",
|
|
" // Keep the size of the canvas, canvas container, and rubber band\n",
|
|
" // canvas in synch.\n",
|
|
" canvas_div.css('width', width)\n",
|
|
" canvas_div.css('height', height)\n",
|
|
"\n",
|
|
" canvas.attr('width', width * mpl.ratio);\n",
|
|
" canvas.attr('height', height * mpl.ratio);\n",
|
|
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
|
|
"\n",
|
|
" rubberband.attr('width', width);\n",
|
|
" rubberband.attr('height', height);\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
|
|
" // upon first draw.\n",
|
|
" this._resize_canvas(600, 600);\n",
|
|
"\n",
|
|
" // Disable right mouse context menu.\n",
|
|
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
|
|
" return false;\n",
|
|
" });\n",
|
|
"\n",
|
|
" function set_focus () {\n",
|
|
" canvas.focus();\n",
|
|
" canvas_div.focus();\n",
|
|
" }\n",
|
|
"\n",
|
|
" window.setTimeout(set_focus, 100);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_toolbar = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var nav_element = $('<div/>')\n",
|
|
" nav_element.attr('style', 'width: 100%');\n",
|
|
" this.root.append(nav_element);\n",
|
|
"\n",
|
|
" // Define a callback function for later on.\n",
|
|
" function toolbar_event(event) {\n",
|
|
" return fig.toolbar_button_onclick(event['data']);\n",
|
|
" }\n",
|
|
" function toolbar_mouse_event(event) {\n",
|
|
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" for(var toolbar_ind in mpl.toolbar_items) {\n",
|
|
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
|
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
|
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
|
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
|
"\n",
|
|
" if (!name) {\n",
|
|
" // put a spacer in here.\n",
|
|
" continue;\n",
|
|
" }\n",
|
|
" var button = $('<button/>');\n",
|
|
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
|
|
" 'ui-button-icon-only');\n",
|
|
" button.attr('role', 'button');\n",
|
|
" button.attr('aria-disabled', 'false');\n",
|
|
" button.click(method_name, toolbar_event);\n",
|
|
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
|
"\n",
|
|
" var icon_img = $('<span/>');\n",
|
|
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
|
|
" icon_img.addClass(image);\n",
|
|
" icon_img.addClass('ui-corner-all');\n",
|
|
"\n",
|
|
" var tooltip_span = $('<span/>');\n",
|
|
" tooltip_span.addClass('ui-button-text');\n",
|
|
" tooltip_span.html(tooltip);\n",
|
|
"\n",
|
|
" button.append(icon_img);\n",
|
|
" button.append(tooltip_span);\n",
|
|
"\n",
|
|
" nav_element.append(button);\n",
|
|
" }\n",
|
|
"\n",
|
|
" var fmt_picker_span = $('<span/>');\n",
|
|
"\n",
|
|
" var fmt_picker = $('<select/>');\n",
|
|
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
|
|
" fmt_picker_span.append(fmt_picker);\n",
|
|
" nav_element.append(fmt_picker_span);\n",
|
|
" this.format_dropdown = fmt_picker[0];\n",
|
|
"\n",
|
|
" for (var ind in mpl.extensions) {\n",
|
|
" var fmt = mpl.extensions[ind];\n",
|
|
" var option = $(\n",
|
|
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
|
|
" fmt_picker.append(option)\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Add hover states to the ui-buttons\n",
|
|
" $( \".ui-button\" ).hover(\n",
|
|
" function() { $(this).addClass(\"ui-state-hover\");},\n",
|
|
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
|
|
" );\n",
|
|
"\n",
|
|
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
|
|
" nav_element.append(status_bar);\n",
|
|
" this.message = status_bar[0];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
|
|
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
|
|
" // which will in turn request a refresh of the image.\n",
|
|
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.send_message = function(type, properties) {\n",
|
|
" properties['type'] = type;\n",
|
|
" properties['figure_id'] = this.id;\n",
|
|
" this.ws.send(JSON.stringify(properties));\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.send_draw_message = function() {\n",
|
|
" if (!this.waiting) {\n",
|
|
" this.waiting = true;\n",
|
|
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
|
" var format_dropdown = fig.format_dropdown;\n",
|
|
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
|
|
" fig.ondownload(fig, format);\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
|
|
" var size = msg['size'];\n",
|
|
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
|
|
" fig._resize_canvas(size[0], size[1]);\n",
|
|
" fig.send_message(\"refresh\", {});\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
|
|
" var x0 = msg['x0'] / mpl.ratio;\n",
|
|
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
|
|
" var x1 = msg['x1'] / mpl.ratio;\n",
|
|
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
|
|
" x0 = Math.floor(x0) + 0.5;\n",
|
|
" y0 = Math.floor(y0) + 0.5;\n",
|
|
" x1 = Math.floor(x1) + 0.5;\n",
|
|
" y1 = Math.floor(y1) + 0.5;\n",
|
|
" var min_x = Math.min(x0, x1);\n",
|
|
" var min_y = Math.min(y0, y1);\n",
|
|
" var width = Math.abs(x1 - x0);\n",
|
|
" var height = Math.abs(y1 - y0);\n",
|
|
"\n",
|
|
" fig.rubberband_context.clearRect(\n",
|
|
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
|
|
"\n",
|
|
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
|
|
" // Updates the figure title.\n",
|
|
" fig.header.textContent = msg['label'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
|
|
" var cursor = msg['cursor'];\n",
|
|
" switch(cursor)\n",
|
|
" {\n",
|
|
" case 0:\n",
|
|
" cursor = 'pointer';\n",
|
|
" break;\n",
|
|
" case 1:\n",
|
|
" cursor = 'default';\n",
|
|
" break;\n",
|
|
" case 2:\n",
|
|
" cursor = 'crosshair';\n",
|
|
" break;\n",
|
|
" case 3:\n",
|
|
" cursor = 'move';\n",
|
|
" break;\n",
|
|
" }\n",
|
|
" fig.rubberband_canvas.style.cursor = cursor;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
|
|
" fig.message.textContent = msg['message'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
|
|
" // Request the server to send over a new figure.\n",
|
|
" fig.send_draw_message();\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
|
|
" fig.image_mode = msg['mode'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
|
" // Called whenever the canvas gets updated.\n",
|
|
" this.send_message(\"ack\", {});\n",
|
|
"}\n",
|
|
"\n",
|
|
"// A function to construct a web socket function for onmessage handling.\n",
|
|
"// Called in the figure constructor.\n",
|
|
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
|
|
" return function socket_on_message(evt) {\n",
|
|
" if (evt.data instanceof Blob) {\n",
|
|
" /* FIXME: We get \"Resource interpreted as Image but\n",
|
|
" * transferred with MIME type text/plain:\" errors on\n",
|
|
" * Chrome. But how to set the MIME type? It doesn't seem\n",
|
|
" * to be part of the websocket stream */\n",
|
|
" evt.data.type = \"image/png\";\n",
|
|
"\n",
|
|
" /* Free the memory for the previous frames */\n",
|
|
" if (fig.imageObj.src) {\n",
|
|
" (window.URL || window.webkitURL).revokeObjectURL(\n",
|
|
" fig.imageObj.src);\n",
|
|
" }\n",
|
|
"\n",
|
|
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
|
|
" evt.data);\n",
|
|
" fig.updated_canvas_event();\n",
|
|
" fig.waiting = false;\n",
|
|
" return;\n",
|
|
" }\n",
|
|
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
|
|
" fig.imageObj.src = evt.data;\n",
|
|
" fig.updated_canvas_event();\n",
|
|
" fig.waiting = false;\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" var msg = JSON.parse(evt.data);\n",
|
|
" var msg_type = msg['type'];\n",
|
|
"\n",
|
|
" // Call the \"handle_{type}\" callback, which takes\n",
|
|
" // the figure and JSON message as its only arguments.\n",
|
|
" try {\n",
|
|
" var callback = fig[\"handle_\" + msg_type];\n",
|
|
" } catch (e) {\n",
|
|
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" if (callback) {\n",
|
|
" try {\n",
|
|
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
|
|
" callback(fig, msg);\n",
|
|
" } catch (e) {\n",
|
|
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
|
|
" }\n",
|
|
" }\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
|
|
"mpl.findpos = function(e) {\n",
|
|
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
|
|
" var targ;\n",
|
|
" if (!e)\n",
|
|
" e = window.event;\n",
|
|
" if (e.target)\n",
|
|
" targ = e.target;\n",
|
|
" else if (e.srcElement)\n",
|
|
" targ = e.srcElement;\n",
|
|
" if (targ.nodeType == 3) // defeat Safari bug\n",
|
|
" targ = targ.parentNode;\n",
|
|
"\n",
|
|
" // jQuery normalizes the pageX and pageY\n",
|
|
" // pageX,Y are the mouse positions relative to the document\n",
|
|
" // offset() returns the position of the element relative to the document\n",
|
|
" var x = e.pageX - $(targ).offset().left;\n",
|
|
" var y = e.pageY - $(targ).offset().top;\n",
|
|
"\n",
|
|
" return {\"x\": x, \"y\": y};\n",
|
|
"};\n",
|
|
"\n",
|
|
"/*\n",
|
|
" * return a copy of an object with only non-object keys\n",
|
|
" * we need this to avoid circular references\n",
|
|
" * http://stackoverflow.com/a/24161582/3208463\n",
|
|
" */\n",
|
|
"function simpleKeys (original) {\n",
|
|
" return Object.keys(original).reduce(function (obj, key) {\n",
|
|
" if (typeof original[key] !== 'object')\n",
|
|
" obj[key] = original[key]\n",
|
|
" return obj;\n",
|
|
" }, {});\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
|
|
" var canvas_pos = mpl.findpos(event)\n",
|
|
"\n",
|
|
" if (name === 'button_press')\n",
|
|
" {\n",
|
|
" this.canvas.focus();\n",
|
|
" this.canvas_div.focus();\n",
|
|
" }\n",
|
|
"\n",
|
|
" var x = canvas_pos.x * mpl.ratio;\n",
|
|
" var y = canvas_pos.y * mpl.ratio;\n",
|
|
"\n",
|
|
" this.send_message(name, {x: x, y: y, button: event.button,\n",
|
|
" step: event.step,\n",
|
|
" guiEvent: simpleKeys(event)});\n",
|
|
"\n",
|
|
" /* This prevents the web browser from automatically changing to\n",
|
|
" * the text insertion cursor when the button is pressed. We want\n",
|
|
" * to control all of the cursor setting manually through the\n",
|
|
" * 'cursor' event from matplotlib */\n",
|
|
" event.preventDefault();\n",
|
|
" return false;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
|
" // Handle any extra behaviour associated with a key event\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.key_event = function(event, name) {\n",
|
|
"\n",
|
|
" // Prevent repeat events\n",
|
|
" if (name == 'key_press')\n",
|
|
" {\n",
|
|
" if (event.which === this._key)\n",
|
|
" return;\n",
|
|
" else\n",
|
|
" this._key = event.which;\n",
|
|
" }\n",
|
|
" if (name == 'key_release')\n",
|
|
" this._key = null;\n",
|
|
"\n",
|
|
" var value = '';\n",
|
|
" if (event.ctrlKey && event.which != 17)\n",
|
|
" value += \"ctrl+\";\n",
|
|
" if (event.altKey && event.which != 18)\n",
|
|
" value += \"alt+\";\n",
|
|
" if (event.shiftKey && event.which != 16)\n",
|
|
" value += \"shift+\";\n",
|
|
"\n",
|
|
" value += 'k';\n",
|
|
" value += event.which.toString();\n",
|
|
"\n",
|
|
" this._key_event_extra(event, name);\n",
|
|
"\n",
|
|
" this.send_message(name, {key: value,\n",
|
|
" guiEvent: simpleKeys(event)});\n",
|
|
" return false;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
|
|
" if (name == 'download') {\n",
|
|
" this.handle_save(this, null);\n",
|
|
" } else {\n",
|
|
" this.send_message(\"toolbar_button\", {name: name});\n",
|
|
" }\n",
|
|
"};\n",
|
|
"\n",
|
|
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
|
|
" this.message.textContent = tooltip;\n",
|
|
"};\n",
|
|
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
|
|
"\n",
|
|
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
|
|
"\n",
|
|
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
|
|
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
|
|
" // object with the appropriate methods. Currently this is a non binary\n",
|
|
" // socket, so there is still some room for performance tuning.\n",
|
|
" var ws = {};\n",
|
|
"\n",
|
|
" ws.close = function() {\n",
|
|
" comm.close()\n",
|
|
" };\n",
|
|
" ws.send = function(m) {\n",
|
|
" //console.log('sending', m);\n",
|
|
" comm.send(m);\n",
|
|
" };\n",
|
|
" // Register the callback with on_msg.\n",
|
|
" comm.on_msg(function(msg) {\n",
|
|
" //console.log('receiving', msg['content']['data'], msg);\n",
|
|
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
|
|
" ws.onmessage(msg['content']['data'])\n",
|
|
" });\n",
|
|
" return ws;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.mpl_figure_comm = function(comm, msg) {\n",
|
|
" // This is the function which gets called when the mpl process\n",
|
|
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
|
|
"\n",
|
|
" var id = msg.content.data.id;\n",
|
|
" // Get hold of the div created by the display call when the Comm\n",
|
|
" // socket was opened in Python.\n",
|
|
" var element = $(\"#\" + id);\n",
|
|
" var ws_proxy = comm_websocket_adapter(comm)\n",
|
|
"\n",
|
|
" function ondownload(figure, format) {\n",
|
|
" window.open(figure.imageObj.src);\n",
|
|
" }\n",
|
|
"\n",
|
|
" var fig = new mpl.figure(id, ws_proxy,\n",
|
|
" ondownload,\n",
|
|
" element.get(0));\n",
|
|
"\n",
|
|
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
|
|
" // web socket which is closed, not our websocket->open comm proxy.\n",
|
|
" ws_proxy.onopen();\n",
|
|
"\n",
|
|
" fig.parent_element = element.get(0);\n",
|
|
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
|
|
" if (!fig.cell_info) {\n",
|
|
" console.error(\"Failed to find cell for figure\", id, fig);\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" var output_index = fig.cell_info[2]\n",
|
|
" var cell = fig.cell_info[0];\n",
|
|
"\n",
|
|
"};\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
|
|
" var width = fig.canvas.width/mpl.ratio\n",
|
|
" fig.root.unbind('remove')\n",
|
|
"\n",
|
|
" // Update the output cell to use the data from the current canvas.\n",
|
|
" fig.push_to_output();\n",
|
|
" var dataURL = fig.canvas.toDataURL();\n",
|
|
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
|
|
" // the notebook keyboard shortcuts fail.\n",
|
|
" IPython.keyboard_manager.enable()\n",
|
|
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
|
|
" fig.close_ws(fig, msg);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
|
|
" fig.send_message('closing', msg);\n",
|
|
" // fig.ws.close()\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
|
|
" // Turn the data on the canvas into data in the output cell.\n",
|
|
" var width = this.canvas.width/mpl.ratio\n",
|
|
" var dataURL = this.canvas.toDataURL();\n",
|
|
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
|
" // Tell IPython that the notebook contents must change.\n",
|
|
" IPython.notebook.set_dirty(true);\n",
|
|
" this.send_message(\"ack\", {});\n",
|
|
" var fig = this;\n",
|
|
" // Wait a second, then push the new image to the DOM so\n",
|
|
" // that it is saved nicely (might be nice to debounce this).\n",
|
|
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_toolbar = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var nav_element = $('<div/>')\n",
|
|
" nav_element.attr('style', 'width: 100%');\n",
|
|
" this.root.append(nav_element);\n",
|
|
"\n",
|
|
" // Define a callback function for later on.\n",
|
|
" function toolbar_event(event) {\n",
|
|
" return fig.toolbar_button_onclick(event['data']);\n",
|
|
" }\n",
|
|
" function toolbar_mouse_event(event) {\n",
|
|
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" for(var toolbar_ind in mpl.toolbar_items){\n",
|
|
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
|
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
|
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
|
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
|
"\n",
|
|
" if (!name) { continue; };\n",
|
|
"\n",
|
|
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
|
|
" button.click(method_name, toolbar_event);\n",
|
|
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
|
" nav_element.append(button);\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Add the status bar.\n",
|
|
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
|
|
" nav_element.append(status_bar);\n",
|
|
" this.message = status_bar[0];\n",
|
|
"\n",
|
|
" // Add the close button to the window.\n",
|
|
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
|
|
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
|
|
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
|
|
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
|
|
" buttongrp.append(button);\n",
|
|
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
|
|
" titlebar.prepend(buttongrp);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._root_extra_style = function(el){\n",
|
|
" var fig = this\n",
|
|
" el.on(\"remove\", function(){\n",
|
|
"\tfig.close_ws(fig, {});\n",
|
|
" });\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
|
|
" // this is important to make the div 'focusable\n",
|
|
" el.attr('tabindex', 0)\n",
|
|
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
|
|
" // off when our div gets focus\n",
|
|
"\n",
|
|
" // location in version 3\n",
|
|
" if (IPython.notebook.keyboard_manager) {\n",
|
|
" IPython.notebook.keyboard_manager.register_events(el);\n",
|
|
" }\n",
|
|
" else {\n",
|
|
" // location in version 2\n",
|
|
" IPython.keyboard_manager.register_events(el);\n",
|
|
" }\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
|
" var manager = IPython.notebook.keyboard_manager;\n",
|
|
" if (!manager)\n",
|
|
" manager = IPython.keyboard_manager;\n",
|
|
"\n",
|
|
" // Check for shift+enter\n",
|
|
" if (event.shiftKey && event.which == 13) {\n",
|
|
" this.canvas_div.blur();\n",
|
|
" event.shiftKey = false;\n",
|
|
" // Send a \"J\" for go to next cell\n",
|
|
" event.which = 74;\n",
|
|
" event.keyCode = 74;\n",
|
|
" manager.command_mode();\n",
|
|
" manager.handle_keydown(event);\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
|
" fig.ondownload(fig, null);\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.find_output_cell = function(html_output) {\n",
|
|
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
|
|
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
|
|
" // IPython event is triggered only after the cells have been serialised, which for\n",
|
|
" // our purposes (turning an active figure into a static one), is too late.\n",
|
|
" var cells = IPython.notebook.get_cells();\n",
|
|
" var ncells = cells.length;\n",
|
|
" for (var i=0; i<ncells; i++) {\n",
|
|
" var cell = cells[i];\n",
|
|
" if (cell.cell_type === 'code'){\n",
|
|
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
|
|
" var data = cell.output_area.outputs[j];\n",
|
|
" if (data.data) {\n",
|
|
" // IPython >= 3 moved mimebundle to data attribute of output\n",
|
|
" data = data.data;\n",
|
|
" }\n",
|
|
" if (data['text/html'] == html_output) {\n",
|
|
" return [cell, data, j];\n",
|
|
" }\n",
|
|
" }\n",
|
|
" }\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"// Register the function which deals with the matplotlib target/channel.\n",
|
|
"// The kernel may be null if the page has been refreshed.\n",
|
|
"if (IPython.notebook.kernel != null) {\n",
|
|
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
|
|
"}\n"
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.Javascript object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"data": {
|
|
"text/html": [
|
|
"<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAgAElEQVR4nOzdd3xUVdoH8BxQwbb2Arqi7uoWXd1Vd93dd5vrEUQQAQ+B0EIv0qVJDUgT6dKbSJfQe69SQwkQegkloUNII3Vmfu8fs/dISJuZe28myfy+n8983vczc++5B3zW5/Hee54TBCIiIiIKKEH+ngARERERFSwWgEREREQBhgUgERERUYBhAUhEREQUYFgAEhEREQUYFoBEREREAYYFIBEREVGAYQFIREREFGBYABIREREFGBaARERERAGGBSARERFRgGEBSERERBRgWAASERERBRgWgEREREQBhgUgERERUYBhAUhEREQUYFgAEhEREQUYFoBEREREAYYFIBEREVGAYQFIREREFGBYABIREREFGBaARERERAGGBSARERFRgGEBSERERBRgWAASERERBRgWgEREREQBhgUgERERUYBhAUhEREQUYFgAEhEREQUYFoBEREREAYYFIBEREVGAYQFIREREFGBYABIREREFGBaARERERAGGBSARERFRgGEBSERERBRgWAASERERBRgWgEREREQBhgUgERERUYBhAUhEREQUYFgAEhEREQUYFoBEREREAYYFIBEREVGAYQFIREREFGBYABIREREFGBaARERERAGGBSARERFRgGEBSERERBRgWAASERERBRgWgEREREQBhgUgERERUYBhAUhEREQUYFgAEhEREQUYFoBEREREAYYFIBEREVGAYQFIREREFGBYAJrgdDoRExOD+Ph4JCQk8MMPP/zwww8/ReATHx+PmJgYOJ1Of5cSfsMC0ISYmBgEBQXxww8//PDDDz9F8BMTE+PvUsJvWACaEB8frwPI3/81ww8//PDDDz/8ePYxbuDEx8f7u5TwGxaAJiQkJCAoKAgJCQn+ngoRERF5iPmbBaApDCAiIqKih/mbBaApDCAiIqKih/mbBaApDCAiIqKih/mbBaApDCAiIqKih/mbBaApDCAiIqKih/mbBaApDCAiIqKih/mbBaApDCAiIqKih/mbBaApDCAiIqKih/mbBaApDCAiIqKih/mbBaApDCAiIqKih/mbBaApDCAiIqKih/mbBaApDCAiIqKih/mbBaApDCAiIqKih/mbBaApDCAiIqKih/mbBaApDCAiIqKih/mbBaApDCAiIqKih/mbBaApDCAiIqKih/m7GBeAW7duReXKlVGmTBkEBQVh8eLFuR7bvHlzBAUFYcSIEV5dgwFERERU9DB/F+MCcNWqVejRowcWLVqUZwG4aNEivP322yhbtiwLQCIiogDA/F2MC8C75VYAxsbG4oUXXsCRI0dQrlw5FoBEREQBgPk7gAtAp9OJDz74ACNHjgQAjwrAtLQ0JCQk6E9MTEzABxAREVFRwwIwgAvAgQMH4qOPPoLL5QLgWQEYFhaGoKCgbJ9ADiAiIqKihgVggBaA+/btw3PPPYdLly7p73gHkIiIKDCwAAzQAnDEiBEQQqBkyZL6ExQUhBIlSqBcuXIej8sAIiIiKnqYvwO0ALx58yaioqKyfMqWLYuuXbvixIkTHo/LACIiIip6mL+LcQGYlJSEyMhIREZGIigoCMOHD0dkZCQuXLiQ4/FcBUxERBQYmL+LcQG4efPmHBdshIaG5ng8C0AiIqLAwPxdjAvAgsAAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIiIiKnqYv1kAmsIAIsqZ0+nEnIGLsHX+TjgcDn9Ph4goC+ZvFoCmMICIcnZg42FIoSCFwtfBw/w9HSKiLJi/WQCawgAiytnW+Tt1ASiFwoZZ2/w9JSIijfmbBaApDCCinO1avi9LAfj5Mw2Rkpzq72kREQFg/gZYAJrCACLK2dlD5yGFQqWHaqPuKy0hhcL3Peb4e1pERACYvwEWgKYwgIhylpmRiYqlakEKhfChyyCFQuWH6yDhVqK/p0ZExPyNYlwAbt26FZUrV0aZMmUQFBSExYsX698yMjLQpUsXvPnmm3jooYdQpkwZ1KtXD5cuXfLqGgwgotw1e7sjpFDYsSQCzf7o/v8ndJzu72kRETF/oxgXgKtWrUKPHj2waNGibAVgfHw8pJSYN28eTpw4gV27duEvf/kL3n33Xa+uwQAiyt3AOiMhhcKcgYuwe4X7ncDKj9RBYlySv6dGRAGO+bsYF4B3u7cAzElERASCgoJw4cIFj8dlABHlbvaAhZBCYVDdUXC5XGj61peQQmFaz7n+nhoRBTjmbxaA2vr16yGE8CoYGEBEuduxJAJSKDR7uyMAYNuCXZBCodqToUi9k+bn2RFRIGP+ZgEIAEhNTcU777yD2rVr5zlOWloaEhIS9CcmJibgA4goNzcu3YIUCuVL1kBywh04HA7U+1UrSKGwaNRKf0+PiAIYC0AWgMjIyMCnn36KP/3pT/kGQlhYGIKCgrJ9AjmAiPJSu1wLSKFwYONhAMDyCesghULIS825RRwR+Q0LwAAvADMyMlC1alW89dZbuHnzZr7j8A4gkXf6qiGQQmHet0sAAOmp6aj+dENIobB1/k4/z46IAhULwAAuAI3i74033sD169d9GpcBRJS3OQMXQQqFAbVH6O+m9ZoLKRRa/aWrH2dGRIGM+bsYF4BJSUmIjIxEZGQkgoKCMHz4cERGRuLChQvIyMhAlSpV8OKLL+LgwYO4cuWK/qSnp3t8DQYQUd72rDoAKRRCX2+jv7t9PR4fP1ATUiic2HvGj7MjokDF/F2MC8DNmzfn+L5eaGgozp07l+NvQUFB2Lx5s8fXYAAR5S0xLknvBxx3LV5/P6jeKEih0L/WcD/OjogCFfN3MS4ACwIDiCh/jd9oDykUdi7bq787HRkNKRQ+fqAm4q7e9uPsiCgQMX+zADSFAUSUv28bjnE3gO6VtQF02793hxQKP/T+0U8zI6JAxfzNAtAUBhBR/haPXgUpFLpXGpDl+y3zdkAKheAyTZCZkemn2RFRIGL+ZgFoCgOIKH9Hd52EFArVn24Il8ulv8/MyESN5xtDCoUt4WwJQ0QFh/mbBaApDCCi/GWkZ+CTB0MghcLFE7FZfpvW090Spv0/e/ppdkQUiJi/WQCawgAi8ky7f/SAFArrZmzJ8v2NS7dQvmQNSKFw/liMn2ZHRIGG+ZsFoCkMICLPjGs/DVIojGkzNdtvvasOhhQK37Wa7IeZEVEgYv5mAWgKA4jIM+tnboUUCm3/3j3bb/vXH4IUClUeq4fUO2l+mB0RBRrmbxaApjCAiDwTe/oypFCoWKoW0tMysvzmdDpR99UvIIXC6u83+WmGRBRImL9ZAJrCACLyjMvlwufPNIQUCsd2n8r2+9xvFkMKhdbvf+WH2RFRoGH+ZgFoCgOIyHPdKw2AFAqLv1uV7be4a/GocL97f+DoqAt+mB0RBRLmbxaApjCAiDw3PWwepFAYHDo6x9/7fD4EUiiM+oKLQYjIXszfLABNYQAReW7nsr2QQqHR79vl+Pu+dQchhcJnj9dHemp6Ac+OiAIJ8zcLQFMYQESeu309HlIoSKGQdDs52+9OpxO1y7WAFAobZ2/zwwyJKFAwf7MANIUBROSder9qBSkU9q8/lOPvM/qGQwqFDv/uVcAzI6JAwvzNAtAUBhCRd/rVHAYpFOYMXJTj79djbuKjEu6dQS6duVLAsyOiQMH8zQLQFAYQkXfmfbsEUij0rTE012O6VugHKRSmdp9dgDMjokDC/M0C0BQGEJF3Dmw8DCkU6rzcMtdjts7fCSkUar3YDA6HowBnR0SBgvmbBaApDCAi7yQn3NGPeOOu3s7xmPS0DFR7MhRSKESsiSzgGRJRIGD+ZgFoCgOIyHuNft8OUijsXrEv12NGt54CKRS+Dh5WgDMjokDB/M0C0BQGEJH3BtUbBSkUZvQNz/WY05HR7r2DS4cgMS6pAGdHRIGA+ZsFoCkMICLvLRi+HFIo9K46ONdjXC4Xmr71JaRQWDZuTQHOjogCAfM3C0BTGEBE3ju45QikUAj5ZfM8jzMKxdbvf1VAMyOiQMH8zQLQFAYQkfdSklL0QpBbV+JyPS7uWjzK3xcMKRQunogtwBkSUXHH/M0C0BQGEJFvPFkIAgDdKw2AFApTvppVQDMjokDA/M0C0BQGEJFvBtV1LwSZ1W9BnscZPQFDftkcTqezgGZHRMUd8zcLQFMYQES+CR+6DFIo9Pl8SJ7Hpaem47PH60MKhchNUQU0OyIq7pi/WQCawgAi8s3+De4dQeq++kW+xw5vOh5SKAwOHV0AMyOiQMD8zQLQFAYQkW8S45IghYIUCgm3EvM8Nmr7cUih8OmjdZF6J62AZkhExRnzNwtAUxhARL6r++oXkELhwMbDeR7ncrlQ95WWkEJh45yfCmh2RFScMX+zADSFAUTku7Dq30IKhfnDluV77LSecyGFQrdPBhTAzIiouGP+ZgFoCgOIyHcz+oZ7/G7fxROxkEKh/H3BuH09vgBmR0TFGfM3C0BTGEBEvtuxJAJSKDR7u6NHx7d8rwukUFg8epXNMyOi4o75mwWgKQwgIt9du3AdUihUuL8m0tMy8j1+4YgV7q3h/tqtAGZHRMUZ8zcLQFMYQES+c7lcqPZkKKRQOH0gOt/jb12JQ/mS7i3kLp+9WgAzJKLiivmbBaApDCAiczp+EAYpFNZM2+TR8V0+6uvRDiJERHlh/mYBaAoDiMicMW2nQgqF8R2meXT8mmmbIIVCo9+3g8vlsndyRFRsMX+zADSFAURkzsrJGyCFQpeP+np0fHJ8MiqWDoEUCmcOnrN3ckRUbDF/swA0hQFEZM7xPacghYJ6rrHH5xj9Ayd1mWnjzIioOGP+LsYF4NatW1G5cmWUKVMGQUFBWLx4cZbfXS4XevXqheeffx6lS5fGhx9+iFOnTnl1DQYQkTmpd9LwUQn3wo64q7c9Omfr/J2QQqHOyy35GJiIfML8XYwLwFWrVqFHjx5YtGhRjgXgN998g8ceewxLlizBoUOHUKVKFbzyyitITU31+BoMICLz6r/WGlIo7F9/yKPj01LS8OmjdSGFwtGdJ2yeHREVR8zfxbgAvNu9BaDL5cLzzz+PIUOG6O/i4+NRqlQpzJ071+NxGUBE5hmPdBeOWOHxOYPqjoIUCmPaTLV0LryjSBQYmL8DtAA8e/YsgoKCEBkZmeW4f/3rX2jbtq3H4zKAiMyb1su9z+/QxuM8Pmf3in2QQiG4TBM4HA7L5uJyueByuRBz6jLmfbsENy/HWTY2ERUezN8BWgDu2LEDQUFBuHz5cpbjatSogeDg4FzHSUtLQ0JCgv7ExMQEfAARmbX5x+2QQqHN3zzf4SMjPQNVn3A3kY7cFGXpfHav3I+PH6gJKRTq/aoVbsTetHR8IvI/FoAsALMcl18BGBYWhqCgoGyfQA4gIrPOH70IKRQ+fbSuV49ghzYaCykURjSbYNlcnE4nGr/RHlIo/fm24RjLxieiwoEFYIAWgL4+AuYdQCLrZWZk6jtuV89f9/i8fesOQgqFz59pCEemNY+BdyyNgBQKVR6rpx8zV3qoNpLjky0Zn4gKBxaAAVoAGotAhg4dqr9LSEjgIhAiP2nyZgdIobB75X6Pz3FkOvD5Mw0hhcK+dQctmUf/kBGQQmFCx+lwuVz6buDyCessGZ+ICgfm72JcACYlJSEyMhKRkZEICgrC8OHDERkZiQsXLgBwt4F5/PHHsXTpUhw+fBifffYZ28AQ+cnXwcMghUL4kKVenTei+USvF5DkxuFwoNpTDSCFQtT24wCAOQMXQQqFXp99Y3p8Iio8mL+LcQG4efPmHN/XCw0NBfBzI+jnnnsOpUqVwocffoiTJ096dQ0GEJE1pofN86mQO7DxMKRQqPZUA2RmZJqaw9FdJyGFQtUnQvUjZWOnkqpPhFq62piI/Iv5uxgXgAWBAURkDWMlcNu/d/fqPEemA+rZRpBCIWJNZP4n5GFGn3BIofB18LAs41f5RT1IoXD6QLSp8Ymo8GD+ZgFoCgOIyBpnD53Xd9q8bcY8ssX/HgM3GmtqDt0+GQApFJaMWZ3l++6V3N8vGL7c1PhEVHgwf7MANIUBRGSN9NR0lC/p3hP41hXvmi9b8RjY5XKh+tPuBSXH92TdE9x4D7B/yAifxiaiwof5mwWgKQwgIuvU/3Urnxo7Oxw/Pwbeu9a31cCXzlyBFAoVS9VCelpGlt8iVh+AFAoNf+v5LkFEVLgxf7MANIUBRGQd41HrsvFrvT53RLMJkEJhWJPxPl1745yfIIVC6/e/yvZb3NXbkELhoxI1kJKU4tP4RFS4MH+zADSFAURknfFf/gApFMa1n+b1ufs3HDbVFNq49netJuf4e3DZplnawxBR0cb8zQLQFAYQkXVWTFwHKRS6fTLA63MdmQ79Dt/+DYe9Pr9L+a8hhcLKSetz/N24O7l49Cqvxyaiwof5mwWgKQwgIusc3HIEUijU+1Urn84f2ngcpFAY1XKS1+cad/iO7sq5F+jU7rMt33eYiPyH+ZsFoCkMICLr3LoSBykUypeskW0hhieMxRrBZZp41bQ54WYipFCQQuFOYs7v+G2YtQ1SKHT4Vy+v50VEhQ/zNwtAUxhARNZxuVy66fL5YzFen5+RnoHPHq/vflfvp2Men3dws/vOY91Xv8j1mFP7z+p3DImo6GP+ZgFoCgOIyFot3+0MKRR2LInw6fxB9UZ5vZBk8XerIIVCzyqDcj0mJTlV3yWMv8H/vRMVdczfLABNYQARWat/reGQQiF86DKfzt+xJAJSKNQu18LjHUWMnUSmdJud53F1X2kJKRQObT3q09yIqPBg/mYBaAoDiMha3/eY415s0XyiT+enpaSh8iN1IIXCib1nPDqn4wdhkEJh3fQteR5nbBW3fMI6n+ZGRIUH8zcLQFMYQETWWvvDZkih0Fn29XmMvmqIR3f0DMYK4GO7T+V53PgO03zuU0hEhQvzNwtAUxhARNaK+ukYpFCo83JLn8cwdvXwZOu25Phk/W5fcnxynscuG7fG/a7gp7m/K0hERQPzNwtAUxhARNa6eTlOb7vmSysYAEhOuIOPH6jp0Wri43tOuVvHlG2a77j71x/insBExQTzNwtAUxhARNZyuVyo/LD7Hb6Yk5d8Hsd4X29W/wV5Hrdu+hZIodDxg7B8x7x6/jqkUPj4gZpe9RkkosKH+ZsFoCkMICLrNflDB0ihELH6gM9jrJy8AVIotHyvS57H6R0+PFh04nA4ULFULUihcDn6qs9zIyL/Y/5mAWgKA4jIer0++wZSKCwdu8bnMeKuxeOjEjUghcK1izdyPe7r4GGQQmH+MM/azjT6fTtIobB37UGf50ZE/sf8zQLQFAYQkfXGtXevtp3Qcbqpcdr/syekUFj83apcj2n+p06QQmHnsr0ejWkUp0vGrDY1NyLyL+ZvFoCmMICIrLdo1EpIodDn8yGmxpk/bBmkUOj037Acf3e5XPj00bqQQuHC8ViPxhz/5Q9sBUNUDDB/swA0hQFEZL2dy/ZCCoUW73Q2Nc7ls1chhUL5+4KRcCsx2++3rrhXHJcv6fmK4yVjVkMKhV6ffWNqbkTkX8zfLABNYQARWS866gKkUKj2ZKjpsZq+9aV7l48Z2Xf5OLzN3XOw7iue9xyMWH0AUig0+UMH03MjIv9h/mYBaAoDiMh6dxJTfm7OnHDH1FjTes2FFAph1b/N9tvqqRshhULXCv08Hi/m5CVIoVD54Toe7zVMRIUP8zcLQFMYQET2qPZkKKRQiD583tQ4J/ed0QVbWkpalt+mfDULUiiM+mKyx+NlpGfo1cW3rsSZmhsR+Q/zNwtAUxhARPZo8U5nSKGwe8U+U+O4XC6EvNQcUijsWp51LKMFzILhy70as3a5FpBC4ciOE6bmRkT+w/zNAtAUBhCRPXpXHWy6F6Dhu1aTIYXCsCbjs3zf8l13kbljaYRX43X8IAxSKKyfudX03IjIP5i/WQCawgAisseYNlMhhcKkLjNNj7Vv3UFIoVDj+cZwOp0A3HcGqzxWD1IonDty0avxhjYaCykUZvQNNz03IvIP5m8WgKYwgIjsET5kKaRQGFB7hOmxMtIzdLF3dNdJAMDt6/F6ocm97wbmZ1a/BZBCYUjDsabnRkT+wfzNAtAUBhCRPTb/uB1SKLT/Z09LxutfazikUJjy1SwAwLHdpyCFQq0Xm3k91oZZ2yCFwpf/6W3J3Iio4DF/swA0hQFEZI8jO05ACoU6L3veoy8vG2e7i7ZGv28HwFwRZ8ytdrkWlsyNiAoe8zcLQFMYQET2uHbhOqRQ+PiBmvq9PTOSbiejwv01IYVC7OnLmNE3HFIoDG3k/WPcm5d/3kEkMyPT9NyIqOAxf7MANIUBRGSPzIxM3W8v7uptS8bs9GEfSKEwf9gyDA4dDSkUZg9Y6PU4LpcLnzwYAikULp25YsnciKhgMX+zADSFAURkn+AyTSCFwqn9Zy0Zb+HIFZBCoeMHYWj3jx6QQmHT3O0+jdXwt20hhcL+DYctmRsRFSzmbxaApjCAiOzT8r0ukEJh57K9lox3Ofqq+9HtfcFQzzWGFAonIk77NFa3TwZACoWVkzdYMjciKljM3ywATWEAEdmnZ5VBkEJh2fi1lo3Z5M0Ouv2LFAoJtxJ9GmdUy0mQQmFq99mWzY2ICg7zNwtAUxhARPYZ2WIipFD4ofePlo1p7P8rhULVJ0J9Hmfet0sghcLAOiMtmxsRFRzmbxaApjCAiOxjrNS9dws3M47uPKELwJbvdfF5nC3hOyGFQtu/d7dsbkRUcJi/WQCawgAiss/KSeshhUKPygMtG9PhcKDyI3X0YhBfnYg4DSkUgss2tWxuBSkjPQPJCXf8PQ0iv2H+ZgFoCgOIyD67lu9z36l7t7Ol4zb43wreVn/u6vMY8TcS9J3E9NR0C2dnL4fDgbnfLEb1pxuiYukQLByxwt9TIvIL5u8ALgAdDgd69uyJl19+GaVLl8arr76Kr7/+Gi6Xy+MxGEBE9jHusvmyXVtemv+pE6RQ+PzZRl797/1uLpdL30mMOXnJ0vnZJSUpBd0q9s+yCEYKhfPHYvw9NaICx/wdwAXggAED8NRTT2HFihU4d+4c5s+fj0ceeQSjRo3yeAwGEJF9jN1AKtxvzW4ghvq/bqWLn4snYn0ex1hRHLEm0rK52SUlKQVt/94dUihUfrgOVk5aj0a/bwcpFLYv3uPv6REVOObvAC4AK1WqhEaNGmX5rnr16qhTp47HYzCAiOyTnpZhul3LvRwOBz5+oKYeN3zIUp/H6vmp9W1q7JCelqF3Qan2VAMc230KAHQz7G0Ldvl5hkQFj/k7gAvAAQMGoFy5cjh58iQA4ODBg3j22Wcxa9asXM9JS0tDQkKC/sTExAR8ABHZ6bPH61v6mPLKuWu6GbTZhSCjW0+BFAqTu860ZG52cDqd6F9rOKRQqPKLeji+55T+zbgDyN1MKBCxAAzgAtDpdKJr164QQuC+++6DEAIDB+a92jAsLAxBQUHZPoEcQER2Cn29DaRQOLT1qCXj7d9wGFIo1H31C10IJt1O9mms+cOWQQqFfjWHWTI3O0zrNRdSKHz8QE3sX39If+9wOFDpodrudxhPXfbjDIn8gwVgABeAc+fOxYsvvoi5c+fi8OHDmDFjBp588kn88MMPuZ7DO4BEBct4TLl1/k5LxlsxcR2kUOj2yQA0fqM9pFDY/KNv+wFvW7gbUii0fv8rS+ZmNaNXoRQKq6duzPJbzMlLkELhkwdD4HA4/DRDIv9hARjABeCLL76IMWPGZPmuX79++M1vfuPxGAwgInuFVRvsfs9u3BpLxpvUZSakUBjTZiomdZ4BKRQG1fN84dfdTh+IhhQK6rnGlszNSueOXETlh92rlMd/mf0/ajf/uN3dCucvvrfCISrKmL8LSQHYvXt33LlTsE1Jn3zySYwbNy7LdwMHDsRrr73m8RgMICJ7DW86HlIozOgbbsl4fdUQSKGwcOQKHNp6FFIoVH+6oU93wRLjkvQdtpTkVEvmZ4XkhDv60Xln2TfHP5tR/I5sMdEPMyTyP+bvQlIAvv/++yhbtiymTZtWYNcMDQ3FCy+8oNvALFq0CE8//TS6dPF8eygGEJG9pnSbre/YWcHoAbh7xT44Mh2o9mQopFA4suOET+MZi1TOHbloyfzMcrlc6FtjKKRQCHmpOeJv5Pzvpo4fhEEKhVVTNhTwDIkKB+bvQlIAAsDMmTPxy1/+Eu+88w62bdtm+/USExPRrl07vPTSS7oRdI8ePZCe7nlXfwYQkb2MhRYDao8wPZbL5UKVX9SDFAoXjrv7//UPGQEpFKZ2n+3TmM3+2FEXlIXBkjGrde/Eo7tO5nhMZkamfjwcHXWhgGdIVDgwfxeiAhAAUlJSEBYWhocffhjVq1fH2bNn/T2lPDGAiOy19ofNkEKha4V+pseKu3obUih8VKKG3r5t/cytkEKh2dsdfRrTeEdxyZjVpudn1ukD0ahYqhakUFgwfOyk6W8AACAASURBVHmuxx3ddVI/+raywTZRUcL8XcgKQENsbCzq1q2LBx98EF26dEFUVFShXKnGACKyl7Ef8Bcm9u01HNlxQj8aNSTcTET5kjUghcK1ize8HnN8h2mQQmFCx+mm52dGSlIKGvzG/d5fr8++yXOLux8HL4EUCr2rDi7AGRIVLszfhaQATEtLw8aNGzF27Fi0bdsWFSpUQLly5VCiRAmUKFECQgiULl0a77zzjr+nmgUDiMheUduPQwqFer9qZXqsdTO2QAqFTv8Ny/J92/9zt5pZPmGd12MuGrUSUij0+XyI6fmZMaThWL1vcn67pvSoPDDfu4RExR3zdyEpAP/2t7+hdOnSeP/991GvXj30798f4eHhOHjwIFJSUhAXF4dNmzZh5MiR/p5qFgwgInudP3rRvYXZk6Gmx/qh94+QQmFYk/FZvp/VfwGkUOhZZZDXY+5cthdSKLR4p7Pp+fnK6PdXvmQNHNxyJM9jHZkOVHnM/R7kqf2F+xUbIjsxfxeSAvD1119HZGTh31D9XgwgInvdvBynixuz76sNrDMSUij8OHhJlu/PHDwHKRQqPVRbvxvoqXNH3AVq1SdCTc3NV9djbqLqE6EeL2Qx3v+r9mRooXythqigMH8XkgKwqGIAEdkrLSVN99pLjvdtyzZDq790hRQK2xbuzvK9y+VCrRebQQqFiNUHvBozJTlVzy8xLsnU/LzldDrR6cM++h3JzIzMfM8x7naGVf+2AGZIVHgxf/u5AOzVqxf27Ssc7RN8wQAispfL5dIrW69duG5qLKPn39lD57P9ZjSc9qXfoHqusV8eqS4csQJSKFR+uI7H+/l2ln0hhcLi0atsnh1R4cb87ecCsGHDhnjmmWfwwgsvoEWLFli1apVXffj8jQFEZD/1bCN3z7rD2Qs3TyXcStR36lLvpGX7ffviPXqxSV4raHPS+q/d3HcWF+zyeX7eOn/0IiqWDnFvkzd+rUfnpKWk6XPOH4uxeYZEhRvzdyF4BOx0OrFt2zZ07twZr7/+Oh599FFUr14d06dPx61bt/w9vTwxgIjsV/+11pBCIeqnYz6PcWz3KUihUPOFpjn+npKUou80XjwR69XYRjPp8CFLfZ6fNzIzMtHy3c6QQqHbJwM8Llj3rTvoboPzy+ZeF7lExQ3zdyEoAO917NgxDB48GH//+99RqlQp/POf/8SQIUMQG+vdv5QLAgOIyH5GsbN75X6fx9gwaxukUOjw7165HtPlo74+tUcxtqsb1XKSz/PzhrGaudpTDXDzcpzH503qMhNSKAxuMNrG2REVDczfhaQAHDVqFC5dupTt++vXr2Pq1KmoUqUKhgzxb5+tnDCAiOxn7Fu7+cftPo8xPWwepFAY2nhcrscsGL7c3Sfwwz5ejb1qygZIofDVx+Z3K8nPyX1nUP6+YJ/+PoxCev3MrTbNjqjoYP4uJAWgEAKvvvoqLl7MuqF6RkZGoV4kwgAisl/PTwdBCoWVk9b7PMaA2iNybAFzt5iTlyCFwscP1MSdxBSPx47cFAUpFEJfb+Pz/DyRnpaBJm92gBQKXwcP8+rc+BsJ+h1Ib+4aEhVXzN+FqABs1qwZXnnllSxF4NWrV1GiRAk/zixvDCAi+xnF2/xhy3weo+V7XSCFwvbFe/I8znjf8KdFu/M87m5Xz1/XhaOdvfWmfDULUiio5xoj/oZ3/87Z/ON2SKHQ5A8dbJodUdHC/F1ICsASJUrg2rVr6NmzZ5Yi8OrVqxBC+Hl2uWMAEdnPaNEy8+v5Pp3vcrn07hfnjlzM89gxbabmuFtIXhwOBz5+oKYlrWpyc3zPKb1nsTfFqWFYE/ff4fgO06yfHFERxPxdyApAwN0b0CgCeQeQiMZ3mAYpFCZ3nenT+XFXb0MKhY9K1Mh3p4+I1Qf0amFvVsoadw4Pbs57KzZfpKemo/Eb7SGFwoDaI7w+3+Vyoc7LLU0vpCEqTpi/C0kBKITQBSAA9O7dG6+88gr27NnDApAowE3rORdSKIxuPcWn8w9vOwYpFOq83DLfY9NT01H54TqQQuHMwXMeX6NrhX6QQmH11I0+zTEvxirjGs83RsLNRK/Pjz19WT+iTkny/N1GouKM+buQFIADBw5EcnLWbZ569+6NZ599lgUgUYCbM3ARpFAY0nCsT+evnroRUih0reDZKl1j0cmcgYs8vsbIFhM93o/XG3ev+vXl0S8ALBu/Nt8WOESBhvm7kBSAuenXrx8ee+wxf08jVwwgIvsZW571D/H+8Sfwc/87T+8gLh27BlIotP9nT4+vET50mXuOtYb7NMecZKRnoNnbHSGFQr+a3q36vVtfNcTUO5RExRHzdyEvAAs7BhCR/VZMXAcpFHpXHezT+b2rDnbvf/udZ/vfGqt6y5esgYRbnj1yNbaS++LPXX2aY05m9A2HFAqfP9MQt6/H+zSGw+FAtacaQAqFoztPWDY3oqKO+ZsFoCkMICL7rZu+xatHuPdq+Nu2kEJh37qDHp9jLLrwtNly9OHz7t05ngz1aY73Onfkol5ZvHHOTz6Pc3LfGUihUOWxenBk2teihqioYf5mAWgKA4jIflvm7fD5HbbMjExUuP9/LVou3vD4vAkdp3u1bVpKcqputOzpXcPcOBwOtP5rN0ih0PPTQab27f1x8BL3OFUGmZoTUXHD/M0C0BQGEJH9jMerrf/azetzL56IhRQKlR+uA6fT6fF5xu4e6tlGHp8XXLYppFA4EXHa63nezXjnscpj9XAj9qapsYzVyQtHrjA1DlFxw/zNAtAUBhCR/fascvfma/FOZ6/P3bE0wqdzMzMyUeUX9bwq6Nr/syekUNg01/c9iy9HX9VtaFZMXOfzOIB7EUmlh2pDCoXoqAumxiIqbpi/WQCawgAist+BjYfd25i96f02ZsYjUF9WEIdV/xZSKMzoG+7R8d82HAMpFGb1W+D1tQB3w2bjjt2X/+lt6tEv8HP/Q/VcY9NjERU3zN8sAE1hABHZ79DWo5BCocFv2nh9rlGUzejjWRF3t5WTN7gfPb//lUfHz+q/wKv3Bu9lLHapWDoEMScv+TTG3Wb0CTfdQoaouGL+ZgFoCgOIyH5Hd56AFAr1ftXK63ONxRRbwnd6fe6N2Jt6CzlP2rAYi1Xa/r2719eKuxav27V404A6L1/+pzekUFg+wdyjZKLiiPmbBaApDCAi+52IOA0pFGqXa+HVeS6XC1Ueq2fqHbjmf+oEKRTWz9ya77GnD0Trvn3eGlhnJKRQaPbHjsjMyPRlqlmkp6ajYukQSKEsuZtIVNwwf7MANIUBRGS/U/vPQgqFWi828+q8G5duuRs63xeM9LQMn65t7MM7oHb+7xCmJKXoVjBJt5PzPd4QsfqAbjxtdgWx4eDmI5BCIbhsU77/5wOHw4GLJ2K9+udIRQvzNwtAUxhARPY7c/Ccu5gp08Sr8/avP+Tzu4OGqJ+O6QbPnjRSDi7TxKuVwynJqaj7SktIoTCu/TSf53mv6WHzPC5c6WcXT8RiTJupqPZkKKRQqPRQbRzfc8rf0yIbMH+zADSFAURkv7OHzuvVrN5Y/N0qU1vIAYAj06GLgajtx/M9vsO/enm1e4fRcLp2uRZISUrxeZ734vt/3jl76Lxe9X3vZ9n4tf6eHtmA+ZsFoCkMICL7GdusqWcbeXXeiOYTIYXClG6zTV2/f63hkELh+x5z8j12aKOxHq86Pn0gGuXvC4YUCrtX7DM1x7ulp2Xgkwfd7/9dOB5r2bjFUezpy+gfMiJLwdezyiCMbfe9XgDEv8PiifmbBaApDCAi+0VHXfCpADQaM2+cvc3U9Y32LM3/1CnfY+d+s9ijR6+OTAdavtfFljYtUduPs/9fPhJuJWJM26l6m0Djn8P5oxdx5dw1VH+6IaRQGNvue39PlWzC/M0C0BQGEJH9fCkAXS4Xqj7hfnR75uA5U9e/fT0eH5WoASkUbly6leexO5a4dx5p+W7eO48sHOne7q3qE6G4dSXO1PzuNWfgIkih0OfzIZaOWxxkZmRi0aiV+rG+FArdKw3A6choAO6FPM3e7uj+Z/heF6Snpvt5xmQX5m8WgKYwgIjspx8Be/EOoF4BXLKGJUm89ftfQQqFVVM25Hnc+WMxkELh00fr5nr37drFG6j8SB3b3tHr9skA7v+bg/3rD6Hh79rpwq/pW19i//pD+vfMjEx89bF7J5YazzfG9Rhz+zBT4cb8zQLQFAYQkf2MRSA1nve8ANy79iCkUGj427aWzMHYVSO/u2oZ6Rn6vb6c7ha6XC70+uwbSKHQ7h894HQ6LZmfwel04rPH60MKhZP7zlg6dlF17eIN9FVDdOGnnm2E5RPWweH4eVW3y+XCN/W/gxQKlR+uw5W/AYD5mwWgKQwgIvsZDZaDyzb1+Jz5w5ZBCoW+yprHoCf2ntF39jLS8+4pWP+11pBCIXJTVLbfti/eAykUKtxf0+fm1HkxHpdXfqSOR21rirOM9AzMHbQIlR+uo/tBjmk7NVtvP5fLhfEdpulj9qw64KcZU0Fi/mYBaAoDiMh+RvEV8svmHp9jZg/gnDidTtR4vjGkUDiw8XCex3avNCDH9iF3ElMQ8svmlqxMzs3yCesghUKnD/vYMn5RcXDzkSyPe9v/syfOHjqf47HG3V0pFNZN31LAMyV/Yf5mAWgKA4jIfsd2n4IUCnVfaenxOS3f7QwpFLYt3G3ZPAY3GA0pFCZ0nJ7nceO//CHHFaRGa5F6v2qFtJQ0y+aVZY6h7jlO6znXlvELu9vX4/XfgfHe6LrpW3J9H3PhiBX62MXfrSrg2ZI/MX+zADSFAURkP6OtSf3XWnt0vCPToffBjT192bJ5bAnf6dF7hSsnb4AUCl0r9NPfndp/FuVLulcSR6yJtGxO9zIePwfaY0yXy4XVUzei2lMNdP++US0n5bmV28pJ63XxN/Pr+QU4WyoMmL8DvACMjY1FnTp18OSTT6J06dJ48803sXfvXo/PZwAR2S9yUxSkUGj0+3YeHW+sxK38cB1LF1kkxyfrvnGXzlzJ9TijYA15yf3I2uFw4Is/d4UUCv1D7NuaLeFmoi5oEuOSbLtOYXPheCw6/LuX/rM3+2NHHN11Ms9z1s/cqlv7TOo8g/0SAxDzdwAXgHFxcShXrhwaNGiAPXv2IDo6GmvXrsWZM56vnGMAEdnPWNHb7I8dPTp+y7wdkEKh9ftfWT6Xjh+EuR8Xjs79cWHCrZ8LsZSkFCwe7d6S7rPH6+PmZWt7/t1t98r9lq58LuzS0zIwo084KpaqpQv+8KHL8l38snX+Tn039rtWk1n8BSjm7wAuALt27Yp//OMfpsZgABHZb8fSCK8KuindZkMKheFNx1s+l/AhS7M93s2Jes69YGT3qgOo8ot67kUh49ZYPp+7/dD7R0ihMDh0tK3XKQyith9Ho9//vMije6UBuHLuWr7n7Vgaoe/iDmk41vI2PFR0MH8HcAH4u9/9Du3bt4dSCs888wz++Mc/YtKkSXmek5aWhoSEBP2JiYkJ+AAispvx7l2Hf/Xy6HijEfLSsdYXXBeOx0IKhYqlaiElKSXX44xHkq3+4m4g3fqv3WwvNowmxnb8uQuL5IQ7GPXF5CyLPDb/uN2ju3gRayL13cKBdUZm6QNIgYcFYAAXgKVKlUKpUqXQrVs3HDhwABMnTkTp0qXxww8/5HpOWFgYgoKCsn0COYCI7LZuhnsv3i7lv/bo+OAyTSCFwtGdJyyfi8vlQr1ftYIUCjuWROR63MgWE3WRUv6+YL3VmF1cLpdeAHFib/FsAL1r+T7dRkcKhaGNxiLhVqJH50ZuisInD4bo3pCB3iORWAACAVwA3n///fjb3/6W5bs2bdrgr3/9a67n8A4gUcEzetv1rjo432Pjrt7Wq0BTklNtmc/o1lPyfcRsNKKWQmF8h2m2zONul6OvQgqFjx+oifS0vBtVFzW3r8djQO0R+u+z3q9a5duL8W5RPx3TzaB7VhmUbyNvCgwsAAO4AHzppZfQuHHWraXGjRuHsmXLejwGA4jIfkavtoF1RuZ7bMSaSNsXQhjXqPlC01wfPQ6qO0rf/buTmPujYqtsne9+TN7yvS62X6uguFwubJy9DdWfbqj3dZ7YaTpS73jeQ/HY7lP6HcyvPu5X7Ipj8h3zdwAXgCEhIdkWgbRv3z7bXcG8MICI7Der3wJIoTCsSf6LOuYOWuRut1JruG3zSU/LQOVH3HeUTu0/m+3388diUOGBmj+vBLbpTuTdJnedadvCF3+4EXsTPasM0n+HTd/60utH26cPRKPqE6HunVH+G+ZV4UjFH/N3ABeAERERuO+++zBgwACcPn0as2fPxkMPPYRZs2Z5PAYDiMh+RnEzrv20fI/tq4ZACoV53y6xdU69qw52bzXXN+tWcy6XS7eK+fh/ReDJffa/k9el/NeQQmHFxHW2X8tOLpcLq6ZswGeP19d/hzP6hnv92DY66oK+c9juHz3yXLBDgYn5O4ALQABYvnw53nzzTZQqVQq//e1v810FfC8GEJH9jFWf03rlv71Z3VdaerRfr1nGbh9f/Llrlu/XTXcvWKn0UG20/mu3Atlf1uVy4fNn3MXOiYjTtl7LTlfPX9eFrNH259yRi16Pc/FErN63udVfuiI54Y4Ns6Wijvk7wAtAsxhARPb7pv53Ht3Vu3snjLy2ALPCrStx+lpGc+eEW4lQzzaCFApzBy3Si0UmdZ5h61xuxN7U7xvatcewnZxOJ5aNX4tPH60LKRQ+eTAE4UOW+rRS93L0Vb1SuNkfO3q8SpgCD/M3C0BTGEBE9uv12TcePd40dgzxdM9gs1r9xb2926opGwAAI5pNgBQKTd7sgMyMTKyYuM6jptFmGTuANH6jva3XscPl6Kvo9N8wXUy3+0cPxJy85NNY12Nuou6rX+htA29fj7d4tlScMH+zADSFAURkvy//0xtSKGz+cXuex80ZaP8CkLvN6Buu29Mc3XVSFzGHth4FAP1dcJkmts5DL3yxcZ9hqzmdTiwdu0Yvpqn8cB0sGrXS52bZcVdvo+Fv2+r/ALBzyz0qHpi/WQCawgAisl+ztztCCoWINZF5HmcszAgfuqxA5nU6Mlo/smzyhy8hhcK3Dcfo31OSUnRRaOfdKKNH3pyBi2y7hpUuR1/VC2WkUOjw7164dOaKz+Ml3ErUMRLyUnNcPX/dwtlSccX8zQLQFAYQkf1CXmqe7wIHl8uF4LJNIYXC4W3HCmReLpcry84U1Z5qkK3QM3YNsXNRSpM/dHDvO7xin23XsEJOd/0Wf7fK1BZ5dxJT0Pr9r/Sd1phTly2cMRVnzN8sAE1hABHZzygYYk/nntyvx/y8EKIg+u4ZjIbPUiisnLwh2+9h1dx3JReOWGHL9R2ZDt1u5sq5a7ZcwwpXzl1Dpw/7WHbXDwDSU9P1+4PVnmqA6KgLFs2WAgHzNwtAUxhARPZKT033aGXvtoW79crPgtTu/3rofnWZmZnZfp8eNg9SKAwOHW3L9S+eiNV308zcSbOLy+XCionr9ArfSg/VNvWunyEzI1M3iv700bpFuv0N+QfzNwtAUxhARPa6u8VJbtuuAcCkzjMghcKIZhMKbG7G6lvjk1MRsmNJhLswfduewnT74j3uLeDe7WzL+GZcj7mJrz7ul3WFrwWPaJ1Op77z+smDITi45YgFs6VAw/zNAtAUBhCRvYyFFjWeb5znce3+4b4Tt/aHzQUyr9Q7abrpdOjrbXJtVH31/HVIoVDh/pq27ENrrHweVHeU5WP7yuVyYd30LXo3j4qlQzB/2DI4HN739ctp7DFtpur/KNi1vHC/90iFF/M3C0BTGEBE9tq3zt3br8kfOuR6TEZ6Bj55MARSKFw8EVsg85rSbbZedWr0+2v61pfZjnO5XKj2VINc9w02a3DoaEihMKvfAsvH9sWtK3G6b6Oxm8eF49b9MzEeqX9UogY2zt5m2bgUeJi/WQCawgAistf6mVshhUJn2TfXY47tPqUXAuT1mNgq549eRIX73Qsvti/eg4RbiSh/XzCkULh89mq2443FD0bDaCsZ281tnb/T8rG9tSV8p95/9+MHamLOwEU+7eaRmyVjVuvCcsmY1ZaNS4GJ+ZsFoCkMICJ7zft2Sb6POBcMXw4pFHpUHmj7fJxOJzr8uxekUOj12Tf6e6Ov3cKR2Vf7Tug4HVIojPpisuXzMQquMwfPWT62pxJuJqJ/yAhdnDX/UyecPXTe0mts/nE7PipRA1IoTA+bZ+nYFJiYv1kAmsIAIrLXuPbT8t1PN6z6t3r/Xbut/n6TXnV7d8PhhSNWQAqFL//TO9s5G+f8pB+HWinpdrIuulKSUiwd21O7V+5HcJkm+p28ab3mIiPd2ncd9284rFvdjPpicoHc5aXij/mbBaApDCAie/WrOSzXO2uA+x079WwjSKEQtf24rXOJv5Gg77jN+3ZJlt+unLvmLoJK1kD8jaz/Pog5dVkvhsjMyN4qxlen9p+FFArqubwXyNjhTmIKhjcdrwvQhr9rZ0srltMHonULma+Dh1mykIQIYP4GWACawgAislfb//XZy+0dN6MPXsXSIbassr3b0EZj9WKPnAq5Zn90b0e2ZtqmLN87nU5Ueaye5Y9qt87fCSkU2vytm2VjeuLQ1qN6BbQUCuM7TENaSprl17kcfVXfXez03zDb//lSYGH+ZgFoCgOIyF7GNnBHd53M8feVkzfonSXsdGjrUV3wHNlxIsdjjBWqPasMyvab8Y7g6qkbLZtT+JClkEJhQO0Rlo2Zl/TUdEzsNF2/i1fn5ZY4uNmeHnwJNxPR4DdtdA/F5Pjcm4AT+YL5mwWgKQwgIvs4Mh16de2NS7dyPMZoCDytZ/YefFbJSM9Ao9+3gxQKw5uOz/W46MPn9d3Ie9/Js6NR9XetJkMKhSndZls2Zm5OH4hGkzc76CJ4aKOxSE64Y8u10lLS9J3f2uVa4EbsTVuuQ4GN+ZsFoCkMICL7GE2UP36gZo5bh7lcLtR8oSmkUDiw8bBt85g9YKF+1y4xLinX41wuF+r/ulWOj6y3LdilV8hapeen7q3Qlk9YZ9mY93JkOjB7wELd9kY91xg7l+217XpOpxN9awyFFAqfPV4f549etO1aFNiYv1kAmsIAIrKP8di1/q9b5fh7zMlL7jtupWrZ8g4aAMSevoyKpd1NpjfMyr/x8MRO7pYv/UOyPpa9dvGGXimbeseauRrvHO5ZdcCS8e4Vc+oy2v69u77r1+fzIbh9Pd6WaxmMO6UVS9Wy7fEyEcD8DbAANIUBRGSftT9szrMJ9PIJ62x9/8/lcqFL+a8hhUKXj/p61H7k6K6TkELh00frIj01PctYwWXddyujfjpmyfw+f8a9ItnqnnsulwvLxq9F5YfrQAqFKo/Vw7rpW2xvv2L88/S02CYyg/mbBaApDCAi+0zrOTfP9+a+Dna3iJnRJ9yW62+YtQ1SKHzyYAhiT1/26Byn04laLzaDFCrbPrVh1QZDCoXwIUtNzy09LUMXS/e2nTHj5uU4dPtkgB6703/DcO3C9fxPNGnfuoP6fc8Zfe3550l0N+ZvFoCmMICI7GPsLpFTweR0OnVPPjv6/yXcStT9BWf1926f3TFtpkIKhcENRmf5fu43i/WjVLPufj/Sqjtz2xbs0vsWVywdggXDl+f47qXVzh+L0W1yBoeOZqNnKhDM3ywATWEAEdmn5XtdIIXCjiUR2X4zmiBX+UU9S5srG4Y2HgcpFBq/0d7rnS2MdxerPhGa5dzD245BCoXgMk1MFznH97j3Pw75ZXNT4wBAcnwyBoeO1nf9WrzTucAWXyTcTES9X7kXzrT/Z0/2+qMCw/zNAtAUBhCRPVwul94B4vyxmGy/zx20KNeee2Yd3HJEF0O+vK/ncDh0A+O7F2ikpaTpLc0uR181Ncedy/ZCCoWW73UxNc7BLUdQu1wLvYvJ1O6zLd/KLTcZ6Rm6P2LdV7+wfYEJ0d2Yv1kAmsIAIrLHjdibetVsTgXJl//pDSkUloxZbel101PTdQPiEc0n+jzOqJaTIIXCtw3HZPm+1V+6WrLIYfXUjZBCoVvF/j6dn56WgUmdZ+imznVf/cL2rfTuNbLFRH0XNzrqQoFem4j5mwWgKQwgInvsX38IUig0+E2bbL8lJ9zRfekunbli6XWn9XIvPAku2xRJt33ffeLg5iM5PgYe134apFAY2cL34hIA5n27BFIoDKo3yutzow+fR7O3O/7c1LnxONxJTMn/RAutmOhe8ftRiRrZFssQFQTmbxaApjCAiOyxcOQKSKHQu+rgbL/9tGi3uz/ga60tvea5Ixf1I9rc9h72lMPh0G1fdq/cr7/fttA99yZvdjA1/uSuMyGFwth233t8jtPpxPxhy1CxVC1IofD5Mw1zfL/SblHbj+u/59kDFhb49YkA5m+ABaApDCAiexiLMHLa4s34bUzbqZZdz+FwoM3fukEKhV6ffWPJStTRradACoVv6n+nv4u7Fq/vvCXcTPR57BHNJnjVAufaxRvo9GEffe0elQci7uptn6/vqxuXbun3I/vVHMYVv+Q3zN8sAE1hABHZw3hXbkt41jtxTqdTFxD71h207HqLv1ul30e7HmPN3rNR24/rMe9uCt3wd+1yXd3sqX413T0QF45cke+xG+f8hM8erw8pFCo/XAfLJ6zzS+GVkZ6h9/ht8ocO2fZLJipIzN8sAE1hABFZz+FwoNJDtSGFQszJS1l+O7H3jN5pw6rVqtcuXEflR9y7Xiwdu8aSMQF3sWqssP1p0W79vXH3bnyHaT6P3a1if0ihsGbaplyPSbqdjAG1R+i7fq3f/woxpzxraG0Hoz/iZ4/X97ixNpFdmL9ZAJrCACKy3vmjF913qx6pk60RsbE7SF9lvpky4G43YxRT7f7Rw/LGx8bewH1rDNXfbZq7HVIoNP9TJ5/HbfcP95206I32pgAAIABJREFUbQt25fh75KYohLzUXK+knh42D45Mh8/XM2vzj9t1IbpjacG/d0h0L+ZvFoCmMICIrLd+5lZIodD2/3pk+63Jmx0ghcL6mVstuda6GVv0zhcXjsdaMubdTh+I1uMnJ9wBANy6EqdXwCbc8u09wOZ/6gQpFCJWH8jyfXpaBiZ2mq7bu9R/rTWO7jpp+s9hRszJS7qn45SvZvl1LkQG5m8WgKYwgIisN6bt1BwXecScugwpFCrcXxOJcUmmrxN39bbe+syu1agul0u/87f2h836+0a/b5ft0bA3jDEPbjmiv4uOuoBmf/y5vcvwpuP9/p5dWkqabjnT8YMwv96FJLob8zcLQFMYQETWa/1X92rcjbOzNks29tLt8lFfS67Tt8ZQ/SjWju3kDDO/np9t3t+1mgwpFEZ9MdmnMeu+0hJSKBzbfQpOpxMLR65AxdIhfm3vkhOj2bN6rjFuXo7z93SINOZvFoCmMICIrJWelqH71N3b5NnYG3j5hHWmr7NtwS59N/H0gWjT4+Xl8tmrequ1G5duAQB2LImAFAqhr2dvdO2Jmi+4ewzuXRuJLuW/1nf9ulca4Jf2Ljkxeh6652ndim0iKzB/swA0hQFEZK2ju07qu1h3tyq5HP1zERV3zdyesfE3EqCebQQpFL7vMcfslD1itD8JH7IUAJAcn4zy9wVDCoWr5697PZ56rrFeUSuFQqWHamPp2DWFpq/e9ZibqPZkKKRQmNRlpr+nQ5QN8zcLQFMYQETWCh+yVDdjvpvx+LfTf8NMX6N/yAi9G0d6mjWtZPKzbPxaSKHQ9K0v9XdGUbhy8gavxrqTmIKKpWvpu2st3+1sywIWXzmdTnT6bxikUPjiz10ta9dDZCXmbxaApjCAiKzVs8qgLHfKDMZCAm+LpXsZ28iVvy8YJyJOmxrLG4lxSfrR9ulI9yPnGX3CIYVCn889b2lzZMcJ1H31C138DWs6vtAVWPOHLdNtfPzZd5AoL8zfLABNYQARWcfhcKDqE+7Hhsf3nNLfnzvi7gv48QPmVv/e/eh3SrfZVkzZK8aiE2P/3uN7Trl3CnmsXr6LUDIzMjGt11yUL+lu7/LR//7v+aMXC2LqHjt35KIudFdMNP+uJpFdmL9ZAGqDBg1CUFAQ2rVr5/E5DCAi65zaf1ZvnXZ3u5DJXWfm+FjYGy6XSxdgTf5QcI9+77Z75X79fmNGegacTic+f6YhpFA4tPVorufFnLqM1u9/pe/6Dao3Cp8/7T4v+vD5AvwT5C0zIxMt3+2sF6MUlvcRiXLC/M0CEAAQERGBl19+GW+99RYLQCI/+XHwEkih0KPyQP2dw+FArRebQQqFrfN35nF23jbO+Umv+j21/6wV0/WaI9OB4LLu1bvbFrr7/w2qO8q9UKLzjGzHu1wurJi4DpUfdm9TV/WJUGz+cTsA6HHsXsHsjVn9F0AKhWpPhrLlCxV6zN8sAJGUlITXXnsN69evx7///W8WgER+0ln2hRQKi0at1N/tW3dQFxW+3rW7EXtTP1qe0Sfcqun6xLib2b3SAAA/b5HW8HdZ/70Tdy1evw8phUKnD/vg2sUb+ve7+wAWBuePxehHv+tmbPH3dIjyxfzNAhD169dH+/btAYAFIJGfpCSn6gLi4omfV7T2rzXcVMNkl8uFrhX66RWpdjZ89oSxm0n5kjVwPeYmkuOTUeH+mpBCIfa0e8HE7hX7dJuXiqVqIXzosmx7FOudQDYfyekyBcrpdOoVzT0qD+SjXyoSmL8DvACcO3cu3nzzTaSmpgLIvwBMS0tDQkKC/sTExAR8ABFZYeeyvZBCoc7LLXUBkXAzUReFvj62XTJmNaRQ+ORBe/b69UWHf/WCFAozv54PAOj0YR9IoTBn0CK9c4bxruLZQzm/49fiHfe7dntWHcjx94JktLj59NG6We5SEhVmLAADuAC8ePEinn32WRw6dEh/l18BGBYWhqCgoGyfQA4gIiuMaDYh252+BcOXQwqFFu909mnMC8dj8cmD7u3RFn+3yqqpmrZ+5lZIoRDyUnM4HA4sGrXSXaQ+VFsXf+PaT0N6anquY3T4t7uI3BLu+3uRVrh1JU43oy5Mf8dE+WEBGMAF4OLFixEUFISSJUvqT1BQEIQQKFmyJByO7JuW8w4gkfWcTieCyzSBFAoRayIBuB/dNvhNG0ihsGz8Wq/HzEjP0HfJulboV6geS6anpqPaUw0ghcKOZXsxsfMMXfgFl22C/esP5TtGz08HWdIX0axB9dyLWFq+1yXHf2cSFVYsAAO4AExMTERUVFSWz3vvvYe6desiKirKozEYQETmHd15Qrd/MRZ67N9wWD9WvJOY4vWYk/5XVFV/uqHef7cwGdd+mntl7/+2SzM+84ct8+h8o/Ca9+0Sm2eau6ifjrl7EpaoUaBNtYmswPwdwAVgTrgIhKjgjf/yB0ihMKD2CP1d76qDfV78sX/9IV1QbV+8x8qpWsLlcmHOwIV6jpUfrYuvg4fp1b6eGNvue0ihMLmrf/bZdTgc+g7rsCbj/TIHIjOYv1kAZsECkKhgOZ1OhLzUPEuxdjn6Kj4q4d7pwtuFG7evx+vHySOaT7Rjyqbcvh6PsOrfZrnrN7LlRFw+e1WvDo67Fp/vOEbPvaGNxhbArLNb/f0mSKHw2eP1cft6/vMlKmyYv1kAmsIAIjLHeIxY5Rf19KIH4+5W1wr9vBrL6XSi2ycDIIVC4zfaI/VOmh1T9tnulftR4/nGelu7b+p9px9Tp6Wk4Ys/d4UUCkvHrsl3rJWT1mdrml1QUu+k6UbU9+7ZTFRUMH+zADSFAURkjrH6d3CD0QCAxLgkVH7EvfPF3rUHvRorfMhS3fKlMG2RlpKcmqW9S+M32uP0gWg4HA7ULtcCUiis/n4TwocugxQKbf+vR75j7l6xz9QKaTPmDFwEKRTqvtLSL1vqEVmB+ZsFoCkMICLfpaem6xYi+zccBvDzo82mb33p1crdo7tO6obKyyess2vKXju66yTqv9Y6S3uXtJSf70wa2981e7sjbsTe1I++r5y7lue4pyOjIYWCeq6x3X+ELBLjkvQ/s/UztxbotYmsxPzNAtAUBhCR7zbN3a774TmdTqTeSYN6thGkUNgwa5vH4yTcTNTvEfavNbxQtHzJzMjEtF5zUb6ku6AL+WVzXeTeLTEuSe/1G7kpSjeFntVvQZ7jJ9xM1EXl3QWl3ab1nKubVN+7OwlRUcL8zQLQFAYQke86/TcMUij80PtHAMDCESv0o0VHpmc95e5+7y/09TZITrhj55Q9cv7oRbR8t7Mu0AbWGYmk28m5Hj+q5SS9P/C66VsghUL911rnWci6XC58+mhdnxbK+CrhViKq/KIepFDYtnB3gVyTyC7M3ywATWEAEfkm5uQlver12oXrSEv5eWHByknrPR5n5tfzIYVCpYdq57ptWkFxOp1YOGIFKpZ27z5S7clQbJm3I9/zYk5d1o9+T+w9o9+BjPrpWJ7nNXu7I6RQ2L1yv1V/hDzN6BOuH1fz7h8VdczfLABNYQAR+WZM26lZVrEa277VLtcCGemeLSyIWH1AF05rpm2yc7r5unr+Ojp+EKbv+nWr2N+rBtRh1dx9D4c0HIshDcdCCoVvG47J85y+NYZCCoUFw5ebnX6+UpJT9e4lm3/cbvv1iOzG/M0C0BQGEJH3khPu6EeJEWsicScxRb/75+nWZpfPXkW1/+2iMaLZBJtnnDuXy4XV32/Sf57KD9fBsvFrvX4P8eiuk7o9zPbFe/RdzeT43B8dG+/jFUQj5qVj10AKhXq/asUt36hYYP5mAWgKA4jIe/OHududNPxdOzidTv1osf5rrT169y8lORVN3/oSUii0/ms3v7UiuXUlDj2rDNJ3/dr+Xw/Enr7s83gd/t0LUiiMbfc9Gr/RPt+egMYimrZ/7+7zNT3hdDoR+rp7X+bFo1fZei2igsL8zQLQFAYQkXcy0jMQ8svm+m7frStx+p03T96Xczqd6KuGQAqF4DJNcCP2ZgHMOrst83boR6IVS9XC3G8Wm74ztnftQX3nb/aABXq1bW53E88duaj3S7bznTxjXlUeq4eUJO/3ZSYqjJi/WQCawgAi8s7qqRt18Zaemo5hTca77+S9/5VHj02nh83Tj0qjth8vgBlnFX8jAf1rDdd3/Vq809myptMul0vvBjK23fe6PcyhrUdzPN6R6dALTszcecyPsXXd6NZTbLsGUUFj/mYBaAoDiMhzjkwH6v+6ld5C7PSBaL2Iw5Nibsu8HbrwWj11YwHMOKsdSyKgnnNv5Vb+vmBM6zXX4wUrHl9jaYT7XcJH6uCb+u6t4vp8PiTX41u//xWkUNg0156FGXHX4nWD7eioC7Zcg+j/27vvsCiuhQ3gu6KgRgG9GmvgJiYmUW/aTUjyJTfmXldARVCzNBURkaLYu7FgiRUVFY1d1KgoURELlljQCIoNVFTsBQtF6SJt9/3+IExYl7LLLm33/T3PPI8ss7NzmCPn5cycc6oD228GQI2wAhGprqj376fmbniVmY0R300pnLzZ2b/c9944dxvdGxT2dq0as6kKzvZv6S8zMK//MiF8Du40GnEX7lbKZ8nlcmEOQb9BK4WpckpbGSRg2Hqhx7Ay7F5aODejj8XESjk+UXVh+80AqBFWICLV5L7OFVbrCF60D4c2nhB6upLiy36O79n9BGGU8FTbeVU6CjUi9DwcWg0Wgti6ib8h93VupX5m5L4LwojiooEhq0YHlrjv8W2nIRFLMfSrygloRbekOfiDdA3bbwZAjbACEakm2C8UErEUTm09kfgoCX2auUEilmLnwr1lvi/9RQbcPhohPG9XVYMQ0l8o9voN6jASN87drpLPlsvlGPbN5MLA23OeMNAjIyVTad/Ex8nCLelXGdr92Tx/kCgE35TENK0em6i6sf1mANQIKxBR+dKS02FnOqDw2b2NJzC331JIxFJ4fDIG+Xn5pb7v9asc4Taxs5mXWhMra+L07nN/P+tnYI+1Eyq/1+9Nl49fhUQshVU9ByEAb/2l5PWB+787BBKxFFFhl7V6DkVL8435cbpWj0tUE7D9ZgDUCCsQUfn8PVdDIpbC6/Nx+DPknBCs4s7fKfU9BfkFwhx7vZq44kHs40o/z5SEVMxyWCz0+rl3HFVlvX4lmWA5CxKxFMO//RkSsRR9mrkhO+u10n6L3H8t8zZxRY3rMqPKVhohqmpsvxkANcIKRFS2uAt3hZG+kfsuwL5lYc/a2vFbSn2PTCbDgoEBkIil6N7AGVdPl70mrqbkcjmObg4X5vWzrOuADT9vq7YJposUHyXt9I6nMHr6TeHBkcLE2tqSnfUa1oaFo3/jbz3V2nGJagq23wyAGmEFIipdQX4BvL8oHNE6t99SYT45946jSr2lKpfLhXWCLes6ICL0fKWe4/MHiZhkPVvo9fP6fBzuXL5fqZ+pjgWuhUHY5f1hkIilkL49SKkXMDM1C5Z1HSARS/HsXoJWPvfi0RhhbWZ1l7Ujqg3YfjMAaoQViKh0QfNDIBFL0bupK4IXhQoTON++dK/E/eVyOdZP3iaEsaNbwivt3AoKCrDb/4CwCkm3+s7YPndPmc8kVofkJy+ECaGLek+D5u1R2m/sf321ers2cFrhOsPzByzXyvGIahq23wyAGmEFIirZwxvxwioVW2b9LszhV9ao36I1gctbA1dTd2MewMdiovBZoztPw+O4J5X2eZr6bdbvhc9CNnUVAnVmapbCPiHLw7S6LnDR84f7Vh3RyvGIahq23wyAGmEFIlKWl5snTGY8oetMuLYfDolYisnd55S6Zm1RyJGIpfh98b5KOa/srNdYO+E34XapnekA7F99tFLX0dWG3Ne56P/e0MKBIM0Lp89ZN/E3hX2Sn74UnhdMeJik0efJ5XJhmp5bFytnwmui6sb2mwFQI6xARMrWTvhN6LEqer7O2cwLacnK/0/kcrlwu1EilmLHgrLnBayocwcuCtOlSMRSzLRfVGXTymjD2f0X/xo97SDcsn4z6I35cTokYim2z1W+RayOl89ThJHaVT39DVFVYfvNAKgRViAiRVFhlxVClkQsRTcjJ9yMUp5ORS6XY824zcL+5U0KXRFJ8S8w4yc/4TP6mnvj7P6LWv+cqjC91wJIxFLYmrgULqHntETh+0Wrq7i2H67RwI0rp64XDjxp56PpKRPVWGy/GQA1wgpE9LfnDxKFqVTGS2YKoetw4AmlfQsKCoT5ASViKfYsO6jVc8nPy0fwon3CIA/Lug5YM25zla0kUhkSHyejZ+P+hT+zOoU/t+JT5LzKyBbKe+XU9Qp/zpFNJ4Xb90S6iu03A6BGWIGICr1+lQOvz8dBIpZi4EcjYG3kVOp8f7k5eULvoKWBPQ6uO6bVc4kJj8XgTqOFcDniuym4d+WhVj+juoQEhP21QohjiaupLB68qsTeQXVsm7MbErEUC91WaOOUiWoktt8MgBphBSIqnLh5ptRPGKFadItyxk9+SgMsMlOzhClLuhk5ITw4UmvnkfzkBeb09ReC30/N3XBow/EaP8hDHTKZDKP+M1UhBAYv+nvQzN2YB8L3kp+8qNBnrBodWO5k3US1HdtvBkCNsAIRAWvHbxHWrS26BTzy+ynIyc5R2C/hYZLQM2dr7IJLx65q5fNzc/IQNG+PcPuzax17LPVeg/SXGVo5fk3z5M4z9GjYVwi6Nm/1w7P7f08APbrzNI0CXNHScqWtPUykC9h+MwBqhBWI9N3upQeEIFI0dYjnp2OV5qm7ce62MJGxYxsP3InWfLUNuVyOMyFRGPC+j8Lt3tImmtYle1ccEsJu0fN6RQM/ikYM2xq7ICMlU+1jz3NZptSzSKRr2H4zAGqEFYj0WdFgAYlYil5NCicpdvtoBFISUhX2O7b1tDAptOenY5H4OFnjz74b8wDjuswQPt+htQeObgnXm2XL5HI5Jnf7RSEEHlhzFEDhbWKPT8ZAIpZi0/Qdah97Xv9llTofI1FNwPabAVAjrECkr45vOw1Lg8LgUTQy1bX9cIW59QryC7BqzCYhpE2zm6/xKNwXz1KwePAq4bO71XfG+snb8Cqj9o7uraiUhFRIW7j/fSu4UT88vfscAHB611mhFzD9hXq3whcNWgmJWIptc3ZXxmkT1QhsvxkANcIKRPro6JZwIYAVLfE2qMNIvHiWIuyTkpAqDPaQiKXY8PM2jQZjZGdmY8uMYOE5P4lYitmOi/H8QaI2ilRrXTwaI/w8JGIphn87GQX5BZDJZMKo7F9HBap1zJUjN5a42giRLmH7zQCoEVYg0jf7fj0s3HK0MiwchTrk3+ORmpQm7BNzMhYOrT2E3sHTu85W+PPy8/Kxb9UROLQaLIScYd9MxrUzN7VRHJ0QODVIIQRu+HkbAODCkcJwaG3oiPjbz1Q+3tZfdnEaGNJ5bL8ZADXCCkT6Qi6XY8uMYCFkFIXAcV1mICv9FYDCW76bfXcKvYPuHUfh0c0nFfo8mUyGkzvOCOsIF61MEb4zQm+e81NVQUEBJljOUrg25w9dBgDhOcGfe8xR+ed2dEt44bX9n28lnjVR9WL7zQCoEVYg0gd5uXlYMDBAoZdJIpZi/oDlyMvNAwA8u5+Akd9PEb630G0FsrNeq/1Zcrkc5w5cFG5fSsRSSN8ehJDlYcJnkbL0FxkKax33/sdAPH+QiMdxT2D9V0+tqj2x18/eEkZrE+kqtt8MgBphBSJdl5KYhtE/TFMKf1tn74JcLodcLkfY+mPCQBBbYxcc23pa7c+Ry+W49McVDP92svAZtsYu2DIzWC8HeFTE/asPFZ6R9Pp8HF6/ysHGKdsLR0q3GqzStDBZ6a+EYxS/tU+kS9h+MwBqhBWIdNnNqNtwNvNSWHvW1tgFZ0KiAACJj5KEW4wSsRSjf5imMCGxKoqCX9HqFhKxFD0a9sXa8VuQlsz/V+qK3HdBIajPcliM169eY+CHhbfS57ksU+k4bh+PhEQsRUTo+Uo+Y6LqwfabAVAjrECki+RyOfYsOwireg4KYWJwp9F4dPMJCgoKEBIQJvT6davvjGC/UBQUFKj1GVFhlzHi/34Wjt+tvjNWjNiAl89Tyj8AlSpkeZjCdVs/eRuuR8YJz2aqsvyev9caSMRSrBi+oQrOmKjqsf1mANQIKxDpmtSkNEy1nad0y3fRoJXIznqN25fuwcdiosLKG+oM9CgoKEB4cCS8vxgvHKN7g8LgV3wOQdJM0fJ8Rdv+1UeFW8F2pgPK7ak9ExIFiViK/u8O4aAb0klsv/U8AM6dOxdffvklGjVqhObNm8POzg5xcXEqv58ViHRJROh5/PT2IIXg0KuJK8J3RiD9RQaWDVkrjP61NXFB6MrDKs/tl/s6FwfX/qEwqtemUT+sGrNJYf5A0g65XI6Fbiv+HhlsYI8TQX8KPa5D/j0eua9zS31/dtZr2LxV+DzhzajbVXjmRFWD7beeB0ArKysEBgYiNjYWMTEx6N69O8zMzJCVlVX+m8EKRLohNSkNc/stVer1m2Q9G8/uJWD30gPo3dRVeH1OX3+VQ1tacjp+m/W7wooVvZu6InBaEJ/xq2QF+QWY8dNC4eduVc8BRzefFNZsXuAaUGbvXlGdWOKxqgrPmqhqsP3W8wD4pqSkJIhEIpw6dUql/VmBqDaTyWQ4tOE4ejcdqNjr19QVYRuOIzw4UqHHzuOTMYg5GavSsR/EPoa/52phpRCJWApnMy/s9j+g8XJwpLq83DyFW/rWRk7YuXAvLOsWPt8ZND+k1PfGnIwt7Kl9qx/SX6q3nBxRTcf2mwFQwZ07dyASiXDt2jWV9mcFotoq7sJdDP9mslKv35y+/ji544zCc37SFu44sOZouYM8CvILcCYkCuMlMxWOOeTf43F822nk5+VXUemoOKUQaOiIZUPXCl+fCDpT4vvkcjk8PxsLiViKzb47q/isiSoX228GQIFMJkOPHj3w3XfflbpPTk4O0tPThS0+Pl7vKxDVLomPkzF/wHKl4Deow0js9AtVmMzZplE/bJq+o9x5+F48S8HW2bv+njJGLIWlgT1m/OSHK6eucxBBDZCfl4+ZUj+F28FTbOYJgfD84egS3xceHClM/8M5AUmXMAAyAAq8vb1hbm6O+Pj4Uvfx9fWFSCRS2vS5AlHtkJacjtVjNwnr9xa/3evvtQbDvp6kMB3Lr6MCkZJYeoNfUFCA84ejMVPqB6t6fx+zTzM3rJv4GxIeJlVh6UgVBQUFWOz+q8L1H/rVRGHuxZJu78tkMgz593g+C0g6hwGQARAA4OPjg7Zt2+L+/ftl7sceQKpt0pLTsX7yVoVn8SRiKbo1cMYUm7lw+3iEWtOxPLufgE3Td6CvubfC8Ub83884uiW8zJGlVP3kcjk2Tt2ucO0GfOAjPOsXfUL58Zdrf94oHElcxx7X/rxRDWdNpH0MgHoeAOVyOXx8fNC6dWvcvq3+VAesQFRTJcW/wMoRG9DNyEmhsbcydMSwbybjp+Zuwmu2Ji5YN/E3pCSklnisrLQsHNpwHGN+nK5wrN5NXbFi+Abcv/qwiktHmgpbf0yY0qfoOc+iPwLOHbiotP+iQSsLw+L7Plyaj3QC2289D4BDhgyBiYkJwsPD8fz5c2HLzlbtFxwrENU0ty7exSyHRehqYK8Q1qwNHTHwwxEKq3s4m3kh2C8UWWnK0x7l5uThTEgUZjksVug97FrHHhMsZ+H49j/Z21fLxZyMRY+3/l472KZx4b8t6zrgcOAJhX2z0rKEXt9fnP35XCfVemy/9TwAlvQ8n0gkQmBgoErvZwWimiA3Jw/Htp7C4E/GKA3u6N7AWZj3rWgb+f0UnNxxRmlUbl5uHqLCLmOh2wrYmQ5QGiSyfe4eJD5OrqZSUmV4dj8BLu2G/j14p9gfCIHTghQm+o6NiBOmj9m5cG81njWR5th+63kA1BQrEFWnhzfisWzoWtg06qcU/Gwa9VPoBezZuD/8vdbgbswDhWPkZOcgIvQ8FgwMQK8mrgrHcGzjgVVjNuH2pXvs8dFhr1/lYHrvBUp1SCKWYqbUT2HexqJ1hrvWsVdpTWGimortNwOgRliBqKqlJadj99IDGPDBMKXGumsde4URuUWDMw6uO6bw3FZacjqObg7HjJ/8lMKjfUt3LBu6DldOXVd5mTeq/eRyOUJXHhJ6+Ipv7h1HCes9y+VyLPdZJzxWEBV2uZrPnKhi2H4zAGqEFYiqQlb6KxwOPAEfi4kKD+4Lwe+N5/36/XMINk7Zjsdxfzfad2MeYPvcPRj5/RRYvrG/s5kXVozYgCunrpc72TPptofXHyuN8C7qUT66ORxA4XQysx0XF44mN3JC5L4L1XzWROpj+80AqBFWIKosqUlpCP31CIZ8OUEp4JW02bd0x3Kfdbj25w3IZDKkJafjRNAZLHRbAYfWHkr7e346FoHTgnDr4l3e3iUFuTl58PdaU2I9m9PXHxkpmcjPy8eMn/xKHTRCVNOx/WYA1AgrEGmLXC7H/asPsWbCFvQv9lB+WZtTW08s91mHmJOxyEzLwvlDl7F2/BZh4l6FHpy3+mFqz3nYt+oIEh9xkmYq37UzN/HT24OU6pJjGw+c3X8R+Xn5CqvKbJyynY8NUK3B9psBUCOsQKSJ9BcZOBJ4AhO6zoRNo/4qhT7PT8di45TtiD5xDecOXMT6SVsx/NvJSs/+ScRSeHwyBqvHbsbFozGcsoUqJDcnD8t91qNrHeW6+IuzP5KfvsD6yduE16bYzEX6y4zqPm2icrH9ZgDUCCsQqSMzNQsnd5yBb5+FkLZ0Vynw2Zq4YHrvBQiatwchAWEIGLYeXp+PU3qOTyKWov+7Q+DnthLHt53Gy+cp1V1c0iGPbsZjUMdRSnWup7ELQpaH4XDgCWG+SGczL1w5db26T5moTGy/GQA1wgpEpZHL5Uh4mISQgDBMspqNPs3dVAp31wJMAAAYm0lEQVR81oaOGP7tZMwfEAB/7zWYbregxGf4JGIpXNr5YKHbChwOPIFn9xOqu8ik4+RyOY5sOglbExelujiow0jsW3VYGJ3etY491ozbjJzsnOo+baISsf1mANQIKxAVyUrLwpnQ81g8eBXcO42GdX0nlQOfxydjMKHrLIzrMgOu7YeXONLXqp4jhn41EStGbEB4cGSZ6/USVabcnDysHrsZlnWV6+noH6Zimu184esBHwzDpWNXq/uUiZSw/WYA1AgrkH5KSUjF8aA/4TfoV3h8OqbEiZjLuqXr9vEIuHcaXWrPnkRcOJXLTPtFCPYLxdXTN/D6FXtSqGbJSMnEbMclkJTwfOBQi4mQFhtAMttxMQcfUY3C9psBUCOsQLrtVUY2Lh+/ho1TtmOi5Sw4m3mVONiitM2qniOkLdxh39Id3croEez3zyHw7b0Av836HVFhl5GSmFbdRSdS2YvnKZhmO6/Euu38jpcwjVG3+s5YN/E3ZKYqrz1NVNXYfjMAaoQVqPaTyWR4/jARx7b/iYDhGzC68zQ4tvGAlaHqQU8ilsLK0BE2jfuXGRC71XeG9xfjsWBgAHYt2Y/Lx69yxCTpjJcJKZjRx6/EeSuL95L3auKKrb/sQlb6q+o+ZdJjbL8ZADXCClQ75Obk4f7Vhzi4/hiW+6zD2P/6wtncG90b9lUr5BU+3C4tcbkshaBn5ITB/xqNWQ6LsWVmME7vOotHN5+gIJ+rbJDuy0p/hSWeq2FtpNzrXXz0eq8mrgicGoTUJPZ4U9Vj+80AqBFWoOonl8uR9iIdMeGx2LP8IJYOWYOx//NF/3ZDYWvsUuKD6traHFoNxujO07B48CoE+4Uict8FxN9+xqBHBKAgvwDBi0LxUzkj4Ls3cIa/52o8iH1c3adMeoTtNwOgRliBKo9cLkf6ywzcjLqNI5tPYuOU7fjFyR/DvpmEvv/0hq2JC6zqld0Tp+lmWdcB/d8binFdZmCJxyoEzQ/Bqd8jcSf6Pl5lZFf3j4io1rh6+jp8vp5U4oCR4tu4//kifGcE8nLzqvuUScex/WYA1AgrkOpkMhlSk9IQd/4OwoMjsNMvFAHD1mOa7TwM/XIC+r07BL3/MRDdGjiXOMlxZWzdGzjDtf1wjOsyA35uK7FlRjAOB55A9IlreP4gkT15RFqWnfUaaydsQa8mrmX+3+zTzA2rRgfi3pWH1X3KpKPYfjMAakSfKpBMJkNW+is8vfMMV07F4uSOM9iz7CDWT9qK+QOWY3K3XzD0q4lw/WA4HFoNhp2pC7o3cC73ebnK2vo0c4PHJ2MwyXo2/NxWInBqEPatOoLIfRdw5/J9pCWnQy6XV/ePlUhv3bvyAJO7zYF1OQOu3DuOQtD8ECQ85DQypD361H6XhgFQAzWlAsnlcuTm5CEzNROJj5Jw/+ojXDtzE+cOXsSJoDPYv/oIgubvwZrxW7DEcxVmOyzBJOvZGPn9VHh9Ng6u7YfD+R0vSN8eBDvTAejxVl9YGzlVW3graetqYI8+zd3g3nEUxv7XF7MdFyNg2Hpsnb0LB9f+gch9FxB3/g4SHyfz9hFRLSKXy3Hu4CX4WEwqt/ff87OxCPYLxdO7z6v7tKmWqyntd3ViANRAUQXq194bbh+PgNtHIzDwwxFwbT8cAz4YhgHv+8ClnQ/6vzsU/f7pDWczLzi19YRjGw/YtxoMaQt39Gnuht7NBqJXE1fYmrigp3F/2DTqh+4N+8K6vhOsDB1hWc+hcGqFcp6fqS1bj4Z94dB6MNw7jsLoztPg22chFrn/irUTfsOOBXsRtv4YzoRE4dqfN/Do5hOkJadDJpNV9+Umokoml8sRERqFoRYTYVnOM77OZl5YN/E3XI+MQ0EBH9cg9TAAMgBqpKgC/Siyq/ZQVRWbtZET7EwHwLGtJ9w+GgEfi0mYYDkLsx0XY/HgVVg1ZhO2zAzGbv8DOLTxBE7vPodLx64i7sJdPLnzDGnJ6cjPy6/uy0ZEtcT1yDhMsv4FPd4qe8omayMnTLCcicOBJ7hMIqmEAZABUCPVFQC7GtjDqp4jujVwhk2jfrBrMgDStwfBqa0nBrzvA/dOozH0ywkY9f1UTLSchWl28zGn71Is8VyFX0cHYsPP27B19i4EL9qH0JWHcWjjCZzccQaR+y7g0rGruH72Fu5ffYhn9xKQkpCK7Mxs9sARUbVKTUrD6vFb4PyOV7m/I3s27o9pdgsQ/nsk0pL1t4Gn0jEAMgBqpKgCndh1GucPR+P84WhcOBKDi0djcOmPK7h07CqiT1xDzMlYXDl1Hdf+vIHYiDjcOHcbcefv4Pale7gb8wD3rz3CwxvxiL/9DM/uJSDxURKSn75ESmIaMlIykZ2ZjdycPIYwIiIU3iq+cvo6pvacB7smA8oNhN0b9sXwbydj/9qjeHY/gQPAiAEQDIAaYQUiIqp+MpkMkfvOY6LVbNiZlB8IuxrYQ9rSHb84LcH5I9Fclk4Psf1mANQIKxARUc0jl8txPTIOc5yXwqHV4BLXJ35zs6zrAPuW7phuNx9nQs8jIyWzuotBlYjtNwOgRliBiIhqh6z0LPy+ZD+GfT0JNo37qfbMdR0pbBr1g3un0Vg1JhB3ou9xIJuOYPvNAKgRViAiotrrZUIqtv2yC0O/moCejfujax3VViHqamCPno37w+3jkfBzX4mYk9eQ+zq3uotDamD7zQCoEVYgIiLdkvM6F8e2ncbUnvPg0GowrMpZqeTNzcrQEX2auWHoVxOwctRG3Iy6xV7DGojtNwOgRliBiIj0Q0ZKBkKWh2GCZAbsW7qXu4Rdac8Z2hq7YMD7PpjScx52+e9H4uMkjkquBmy/GQA1wgpERKTfCgoKcP7QZfgNWgH3DqNga+JS4WU0Les6wKZRPzi19cSI76Zgmc9aRO6/iNevXld3MXUO228GQI2wAhERUWny8/Nx4WgM/L3XwOuLcejTzA3WRo4aLetpWdcBPRr2hbSFOzz+NQbTey3A1jm7cOviXS6Jpwa23wyAGmEFIiKiisrKyMLJnWewwDUAXp+Pg/TtQejWwFmlaWtUGajSrb4TejVxRb93h2DYN5Pwi7M/di4KRdyFO8jP1++wyPabAVAjrEBERFSZnj9IxJ7lBzHLcTE8PxsL+5bu6PFWX1jW1TwkFp/uxrKuA7o3dEavpq7oa+4N7y8nYJrtPKweuxl//BaOhIeJOvWsIttvBkCNsAIREVF1k8lkuBN9H0Hz92CmwyJ4fzEeTm090dO4P6yNHLXSo1haL6O1kSNsGvVDn2Zu6PfPIfD6fBwmWc3GYo9V2OkXggtHopGeklHdPyIlbL8ZADXCCkRERLWJTCbDg+uPsXdlGBYP/hVjfvTFwI9GQPr2INg06gdrw8oLjArhsY49LOs6wNrICTaN+qH3PwbCqa0n3DqMxIjvfsb03gux3Gcddi7cizMhUXhy77lWn3Fk+80AqBFWICIi0nU5OTm4FnEDwYtDsWjwrxgvmYnB/xoDp7ae6NXEFd0bOsOqXtUEx5JuX3c1sIdVPQd0q+8Mm8b90KupKxxae8DlfR94fTYWoztPw4yf/LDUew22zNqJwxuPI/JwlN633wyAGmAAJCIiKl3qizREhV3CDr8Q+Hutxs895mCoxUS4vO8D+5busDVxKQyQRT2PGoyQVmf7UWSn9+03A6AGGACJiIgq16tXr3Hj3C0cCjyOjdOCsNBtBSZ3n4NhX0+C20cj4WzmhT7N3dDT2AXdGxSGScu6DmUu7ccAyACoEQZAIiKi2kUul+PBnUd6334zAGqAAZCIiKj2YfvNAKgRViAiIqLah+03AyBWrFgBc3NzGBkZwcLCAlFRUSq/lxWIiIio9mH7recBcMeOHTA0NMTGjRtx/fp1eHh4wNTUFImJiSq9nxWIiIio9mH7recB0MLCAj4+PsLXMpkMrVu3xrx581R6PysQERFR7cP2W48DYG5uLgwMDBASEqLw+oABA2Bra1vie3JycpCeni5sjx8/hkgkQnx8vMLr3Lhx48aNG7eau8XHx0MkEiEtLa0qIkeNpLcB8OnTpxCJRIiMjFR4ffz48bCwsCjxPb6+vhCJRNy4cePGjRs3Hdju3btXFZGjRmIAVCMAvtkD+OhR4TxCjx8/rva/ZqrjLyd96/lkuVlufdhYbpZbH7aiO3ipqalVETlqJL0NgBW5Bfym9HT9fIaA5Wa59QHLzXLrA5Zbv8pdnN4GQKBwEMiwYcOEr2UyGdq0acNBIOVguVlufcBys9z6gOXWr3IXp9cBcMeOHTAyMsKmTZtw48YNeHp6wtTUFAkJCSq9X18rEMvNcusDlpvl1gcst36Vuzi9DoAAEBAQADMzMxgaGsLCwgLnzp1T+b05OTnw9fVFTk5OJZ5hzcNys9z6gOVmufUBy61f5S5O7wMgERERkb5hACQiIiLSMwyARERERHqGAZCIiIhIzzAAEhEREekZBsA3rFixAubm5jAyMoKFhQWioqLK3D84OBgffvghjIyM0KlTJxw8eFDh+3K5HNOmTUPLli1Rv359dOnSBbdv367MIlSIOuVeu3Ytvv/+e5iamsLU1BRdunRR2t/V1VVpyR0rK6vKLoba1Cl3YGCgUpmMjIwU9tHF6925c+cSl1Dq3r27sE9Nv96nTp2CjY0NWrVqBZFIpDQBfElOnjyJzz//HIaGhmjXrh0CAwOV9lH390VVU7fcu3fvhkQiQbNmzdC4cWN88803OHz4sMI+JS2J+eGHH1ZmMdSmbrlPnjxZYh1//vy5wn66dr1L+n8rEonQoUMHYZ/acL3nzp2LL7/8Eo0aNULz5s1hZ2eHuLi4ct+nK+13RTEAFrNjxw4YGhpi48aNuH79Ojw8PGBqaorExMQS94+IiICBgQEWLlyIGzduYOrUqahXrx6uXbsm7DN//nyYmJhg7969uHLlCmxtbfHuu+/i9evXVVWscqlb7r59+2LlypWIjo7GzZs3MXDgQJiYmODJkyfCPq6urrC2tsbz58+FLSUlpaqKpBJ1yx0YGAhjY2OFMr05Z6QuXu+XL18qlDk2NhYGBgYKgaimX++wsDBMmTIFe/bsUalhvH//Pho2bIgxY8bgxo0bCAgIgIGBgUIYUvfnWB3ULffIkSOxYMECnD9/Hrdv38bkyZNRr149XL58WdjH19cXHTt2VLjWycnJlV0Utahb7qIAeOvWLYVyyWQyYR9dvN5paWkK5Y2Pj0fTpk3h6+sr7FMbrreVlRUCAwMRGxuLmJgYdO/eHWZmZsjKyir1PbrSfmuCAbAYCwsL+Pj4CF/LZDK0bt261JVBHBwc0KNHD4XXvv76a3h5eQEo/OuhZcuW8PPzE76flpYGIyMjBAUFVUIJKkbdcr+poKAAjRs3xubNm4XXXF1dYWdnp/Vz1SZ1yx0YGAgTE5NSj6cv19vf3x+NGzdW+OVaG653EVUaxgkTJqBjx44Krzk6Oir0amr6c6xqqvZ8vqlDhw6YOXOm8LWvry8+/fRTbZ5apVInAJa1Lqw+XO+QkBCIxWI8fPhQeK22XW8ASEpKgkgkwqlTp0rdR1fab00wAP6lImsDv/POO/D391d4bfr06fjkk08AAPfu3YNIJEJ0dLTCPj/88ANGjBihxbOvOG2siZyRkYH69etj//79wmuurq4wMTFB8+bN0b59e3h7e+PFixdaPXdNVKTcgYGBMDAwgJmZGdq2bQtbW1vExsYK39eX692pUyd4eHgovFbTr3dxqjSM//nPfzBy5EiF1zZu3AhjY2MA2vk5VrWKBAKZTIZ33nkHAQEBwmu+vr5o2LAhWrVqhXfffRd9+/bFo0ePtH26WqNOADQ3N0fLli0hkUhw5swZ4fv6cr1tbGzQtWtXhddq2/UGgDt37kAkEin05r1JF9pvTTEA/uXp06cQiUSIjIxUeH38+PGwsLAo8T316tXD9u3bFV5buXIl3n77bQCFXcwikQjPnj1T2Mfe3h4ODg5aPPuKq0i53zRkyBC89957Ct3iQUFBCA0NxdWrVxESEoKPP/4YX331FQoKCrR6/hVVkXJHRkZi8+bNiI6ORnh4OGxsbGBsbIz4+HgA+nG9o6KiIBKJlJ59qunXuzhVGsYPPvgAc+fOVXjt4MGDEIlEyM7O1sr/m6pWkUCwYMECNGnSROE2Z1hYGIKDg3HlyhUcPnwY3377LczMzJCRkaHtU9YKVcodFxeH1atX4+LFi4iIiICbmxvq1q2LS5cuAdDO78mqpu71fvr0KQwMDLBz506F12vb9ZbJZOjRowe+++67MvfThfZbUwyAf2EArNgvtnnz5qFJkya4cuVKmfsV/TV17Ngxjc5XW7TxCz0vLw/t2rXD1KlTAejH9fb09MS//vWvcverade7OAZA1Wzbtg0NGzbEH3/8UeZ+qampMDY2xvr16zU9xUpR0VvfP/zwA/r37w9APwLg3Llz8Y9//AO5ubll7lfTr7e3tzfMzc2FP8xLowvtt6YYAP/CW8Dq39rw8/ODiYkJLly4oNJnNWvWDKtXr67wuWqTtm7pSKVSODk5AdD9652VlQVjY2MsXbpUpc+qSde7ON4CLl9QUBAaNGiAAwcOqLT/l19+iUmTJmlyepWmogFw3Lhx+OabbwDo/vWWy+V4//33MWrUKJX2r6nX28fHB23btsX9+/fL3VcX2m9NMQAWY2FhgWHDhglfy2QytGnTpsxBIDY2Ngqvffvtt0oPkS5atEj4fnp6eo17iFTdcgOFt4aMjY1x9uxZlT4jPj4eYrEYoaGhGp+vtlSk3MUVFBTgww8/xOjRowHo9vUGCp+BNDIyUunZvpp4vYuoOgikU6dOCq85OzsrDQLRpP5UNVUDwfbt21G/fn3s3btXpeNmZmaiSZMmWLZsmaanWCkqGgAlEgl69+4tfK2r1xv4+xnIsp6ZK1ITr7dcLoePjw9at26t8jQtutJ+a4IBsJgdO3bAyMgImzZtwo0bN+Dp6QlTU1Nhqg8XFxeFv3oiIiJQt25dLFq0CDdv3oSvr2+Jw8hNTU2F56Ps7Oxq3DBydcs9f/58GBoaYteuXQpTA2RmZgIo/AUxbtw4nD17Fg8ePMCxY8fwxRdf4IMPPkBOTk61lLEk6pZ75syZOHLkCO7du4dLly7ByckJ9evXx/Xr14V9dPF6F/n+++/h6Oio9HptuN6ZmZmIjo5GdHQ0RCIRlixZgujoaOFh9kmTJsHFxUXYv2gamPHjx+PmzZtYuXJlidPAlPVzrAnULfe2bdtQt25drFy5UuH/dlpamrDP2LFjER4ejgcPHiAiIkKYNzApKanKy1cadcvt7++PvXv34s6dO7h27RpGjhyJOnXqKDzCoIvXu0j//v3x9ddfl3jM2nC9hwwZAhMTE4SHhyvU2+zsbGEfXW2/NcEA+IaAgACYmZnB0NAQFhYWOHfunPC9zp07w9XVVWH/4OBgtG/fHoaGhujYsWOpE0m2aNECRkZG6NKlC27dulUVRVGLOuU2NzcvcfLQormjsrOzYWlpiebNm6NevXowNzeHh4dHjfpFWUSdco8aNUrYt0WLFujevbvC/GiAbl5voPAheZFIhKNHjyodqzZc79Im+i0qp6urKzp37qz0ns8++wyGhoZ47733SpwIuqyfY02gbrlLm/S7eH1wdHREq1atYGhoiDZt2sDR0RF3796t2oKVQ91yL1iwAO3atUP9+vXRtGlT/Pjjjzhx4oTScXXtegOFU5s0aNAAa9euLfGYteF6l1RmkUik8H9Wl9vvimIAJCIiItIzDIBEREREeoYBkIiIiEjPMAASERER6RkGQCIiIiI9wwBIREREpGcYAImIiIj0DAMgERERkZ5hACQiIiLSMwyARERERHqGAZCIiIhIzzAAEhEV4+3tje+++67E77Vp0wbz5s2r4jMiItI+BkAior/ExsaiTp06iIiIKPH7EokEjo6OVXxWRETaxwBIRPQXV1dXfP3116V+38HBAZ07d666EyIiqiQMgEREAPLz89G4cWMsXLhQeM3T0xPr168Xvu7WrRusra2r4/SIiLSKAZCICEBcXBxEIhEOHToEAJDJZGjSpAl27twp7NOmTRuMGTOmuk6RiEhrGACJiACcO3cOIpEIf/75JwAgLCwMIpEIoaGhAICzZ88qfJ+IqDZjACQiAvD8+XOIxWIMHToUly9fRocOHdCjRw8MGjQIly9fxqeffgqJRFLdp0lEpBUMgEREf5k7dy6MjY3RokULbNiwATExMTA3N8dbb70FJycnpKSkVPcpEhFpBQMgERERkZ5hACQiIiLSMwyARERERHqGAZCIiIhIzzAAEhEREekZBkAiIiIiPcMASERERKRnGACJiIiI9AwDIBEREZGeYQAkIiIi0jMMgERERER6hgGQiIiISM8wABIRERHpmf8Hiu27JMMc3UoAAAAASUVORK5CYII=\" width=\"640\">"
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.HTML object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"gamma = 1\n",
|
|
"alpha = 1\n",
|
|
"beta_mul = 0.01\n",
|
|
"delta = 0.1\n",
|
|
"\n",
|
|
"z, omega = np.meshgrid(\n",
|
|
" np.linspace(0, 15, 500),\n",
|
|
" np.linspace(0, 2, 500)\n",
|
|
")\n",
|
|
"\n",
|
|
"plt.figure()\n",
|
|
"for beta in (-0.3, 0.0, 1, 4):\n",
|
|
" G = right(gamma, alpha, beta*beta_mul, delta, z, omega)\n",
|
|
" plt.contour(omega, z, (z-G), [0])\n",
|
|
" plt.xlabel(\"$\\omega$\")\n",
|
|
" plt.ylabel(\"$z/\\gamma$\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"application/javascript": [
|
|
"/* Put everything inside the global mpl namespace */\n",
|
|
"window.mpl = {};\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.get_websocket_type = function() {\n",
|
|
" if (typeof(WebSocket) !== 'undefined') {\n",
|
|
" return WebSocket;\n",
|
|
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
|
|
" return MozWebSocket;\n",
|
|
" } else {\n",
|
|
" alert('Your browser does not have WebSocket support.' +\n",
|
|
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
|
|
" 'Firefox 4 and 5 are also supported but you ' +\n",
|
|
" 'have to enable WebSockets in about:config.');\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
|
|
" this.id = figure_id;\n",
|
|
"\n",
|
|
" this.ws = websocket;\n",
|
|
"\n",
|
|
" this.supports_binary = (this.ws.binaryType != undefined);\n",
|
|
"\n",
|
|
" if (!this.supports_binary) {\n",
|
|
" var warnings = document.getElementById(\"mpl-warnings\");\n",
|
|
" if (warnings) {\n",
|
|
" warnings.style.display = 'block';\n",
|
|
" warnings.textContent = (\n",
|
|
" \"This browser does not support binary websocket messages. \" +\n",
|
|
" \"Performance may be slow.\");\n",
|
|
" }\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.imageObj = new Image();\n",
|
|
"\n",
|
|
" this.context = undefined;\n",
|
|
" this.message = undefined;\n",
|
|
" this.canvas = undefined;\n",
|
|
" this.rubberband_canvas = undefined;\n",
|
|
" this.rubberband_context = undefined;\n",
|
|
" this.format_dropdown = undefined;\n",
|
|
"\n",
|
|
" this.image_mode = 'full';\n",
|
|
"\n",
|
|
" this.root = $('<div/>');\n",
|
|
" this._root_extra_style(this.root)\n",
|
|
" this.root.attr('style', 'display: inline-block');\n",
|
|
"\n",
|
|
" $(parent_element).append(this.root);\n",
|
|
"\n",
|
|
" this._init_header(this);\n",
|
|
" this._init_canvas(this);\n",
|
|
" this._init_toolbar(this);\n",
|
|
"\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" this.waiting = false;\n",
|
|
"\n",
|
|
" this.ws.onopen = function () {\n",
|
|
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
|
|
" fig.send_message(\"send_image_mode\", {});\n",
|
|
" if (mpl.ratio != 1) {\n",
|
|
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
|
|
" }\n",
|
|
" fig.send_message(\"refresh\", {});\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.imageObj.onload = function() {\n",
|
|
" if (fig.image_mode == 'full') {\n",
|
|
" // Full images could contain transparency (where diff images\n",
|
|
" // almost always do), so we need to clear the canvas so that\n",
|
|
" // there is no ghosting.\n",
|
|
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
|
|
" }\n",
|
|
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
|
|
" };\n",
|
|
"\n",
|
|
" this.imageObj.onunload = function() {\n",
|
|
" fig.ws.close();\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.ws.onmessage = this._make_on_message_function(this);\n",
|
|
"\n",
|
|
" this.ondownload = ondownload;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_header = function() {\n",
|
|
" var titlebar = $(\n",
|
|
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
|
|
" 'ui-helper-clearfix\"/>');\n",
|
|
" var titletext = $(\n",
|
|
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
|
|
" 'text-align: center; padding: 3px;\"/>');\n",
|
|
" titlebar.append(titletext)\n",
|
|
" this.root.append(titlebar);\n",
|
|
" this.header = titletext[0];\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_canvas = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var canvas_div = $('<div/>');\n",
|
|
"\n",
|
|
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
|
|
"\n",
|
|
" function canvas_keyboard_event(event) {\n",
|
|
" return fig.key_event(event, event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
|
|
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
|
|
" this.canvas_div = canvas_div\n",
|
|
" this._canvas_extra_style(canvas_div)\n",
|
|
" this.root.append(canvas_div);\n",
|
|
"\n",
|
|
" var canvas = $('<canvas/>');\n",
|
|
" canvas.addClass('mpl-canvas');\n",
|
|
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
|
|
"\n",
|
|
" this.canvas = canvas[0];\n",
|
|
" this.context = canvas[0].getContext(\"2d\");\n",
|
|
"\n",
|
|
" var backingStore = this.context.backingStorePixelRatio ||\n",
|
|
"\tthis.context.webkitBackingStorePixelRatio ||\n",
|
|
"\tthis.context.mozBackingStorePixelRatio ||\n",
|
|
"\tthis.context.msBackingStorePixelRatio ||\n",
|
|
"\tthis.context.oBackingStorePixelRatio ||\n",
|
|
"\tthis.context.backingStorePixelRatio || 1;\n",
|
|
"\n",
|
|
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
|
|
"\n",
|
|
" var rubberband = $('<canvas/>');\n",
|
|
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
|
|
"\n",
|
|
" var pass_mouse_events = true;\n",
|
|
"\n",
|
|
" canvas_div.resizable({\n",
|
|
" start: function(event, ui) {\n",
|
|
" pass_mouse_events = false;\n",
|
|
" },\n",
|
|
" resize: function(event, ui) {\n",
|
|
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
|
" },\n",
|
|
" stop: function(event, ui) {\n",
|
|
" pass_mouse_events = true;\n",
|
|
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
|
" },\n",
|
|
" });\n",
|
|
"\n",
|
|
" function mouse_event_fn(event) {\n",
|
|
" if (pass_mouse_events)\n",
|
|
" return fig.mouse_event(event, event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" rubberband.mousedown('button_press', mouse_event_fn);\n",
|
|
" rubberband.mouseup('button_release', mouse_event_fn);\n",
|
|
" // Throttle sequential mouse events to 1 every 20ms.\n",
|
|
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
|
|
"\n",
|
|
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
|
|
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
|
|
"\n",
|
|
" canvas_div.on(\"wheel\", function (event) {\n",
|
|
" event = event.originalEvent;\n",
|
|
" event['data'] = 'scroll'\n",
|
|
" if (event.deltaY < 0) {\n",
|
|
" event.step = 1;\n",
|
|
" } else {\n",
|
|
" event.step = -1;\n",
|
|
" }\n",
|
|
" mouse_event_fn(event);\n",
|
|
" });\n",
|
|
"\n",
|
|
" canvas_div.append(canvas);\n",
|
|
" canvas_div.append(rubberband);\n",
|
|
"\n",
|
|
" this.rubberband = rubberband;\n",
|
|
" this.rubberband_canvas = rubberband[0];\n",
|
|
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
|
|
" this.rubberband_context.strokeStyle = \"#000000\";\n",
|
|
"\n",
|
|
" this._resize_canvas = function(width, height) {\n",
|
|
" // Keep the size of the canvas, canvas container, and rubber band\n",
|
|
" // canvas in synch.\n",
|
|
" canvas_div.css('width', width)\n",
|
|
" canvas_div.css('height', height)\n",
|
|
"\n",
|
|
" canvas.attr('width', width * mpl.ratio);\n",
|
|
" canvas.attr('height', height * mpl.ratio);\n",
|
|
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
|
|
"\n",
|
|
" rubberband.attr('width', width);\n",
|
|
" rubberband.attr('height', height);\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
|
|
" // upon first draw.\n",
|
|
" this._resize_canvas(600, 600);\n",
|
|
"\n",
|
|
" // Disable right mouse context menu.\n",
|
|
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
|
|
" return false;\n",
|
|
" });\n",
|
|
"\n",
|
|
" function set_focus () {\n",
|
|
" canvas.focus();\n",
|
|
" canvas_div.focus();\n",
|
|
" }\n",
|
|
"\n",
|
|
" window.setTimeout(set_focus, 100);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_toolbar = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var nav_element = $('<div/>')\n",
|
|
" nav_element.attr('style', 'width: 100%');\n",
|
|
" this.root.append(nav_element);\n",
|
|
"\n",
|
|
" // Define a callback function for later on.\n",
|
|
" function toolbar_event(event) {\n",
|
|
" return fig.toolbar_button_onclick(event['data']);\n",
|
|
" }\n",
|
|
" function toolbar_mouse_event(event) {\n",
|
|
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" for(var toolbar_ind in mpl.toolbar_items) {\n",
|
|
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
|
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
|
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
|
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
|
"\n",
|
|
" if (!name) {\n",
|
|
" // put a spacer in here.\n",
|
|
" continue;\n",
|
|
" }\n",
|
|
" var button = $('<button/>');\n",
|
|
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
|
|
" 'ui-button-icon-only');\n",
|
|
" button.attr('role', 'button');\n",
|
|
" button.attr('aria-disabled', 'false');\n",
|
|
" button.click(method_name, toolbar_event);\n",
|
|
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
|
"\n",
|
|
" var icon_img = $('<span/>');\n",
|
|
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
|
|
" icon_img.addClass(image);\n",
|
|
" icon_img.addClass('ui-corner-all');\n",
|
|
"\n",
|
|
" var tooltip_span = $('<span/>');\n",
|
|
" tooltip_span.addClass('ui-button-text');\n",
|
|
" tooltip_span.html(tooltip);\n",
|
|
"\n",
|
|
" button.append(icon_img);\n",
|
|
" button.append(tooltip_span);\n",
|
|
"\n",
|
|
" nav_element.append(button);\n",
|
|
" }\n",
|
|
"\n",
|
|
" var fmt_picker_span = $('<span/>');\n",
|
|
"\n",
|
|
" var fmt_picker = $('<select/>');\n",
|
|
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
|
|
" fmt_picker_span.append(fmt_picker);\n",
|
|
" nav_element.append(fmt_picker_span);\n",
|
|
" this.format_dropdown = fmt_picker[0];\n",
|
|
"\n",
|
|
" for (var ind in mpl.extensions) {\n",
|
|
" var fmt = mpl.extensions[ind];\n",
|
|
" var option = $(\n",
|
|
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
|
|
" fmt_picker.append(option)\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Add hover states to the ui-buttons\n",
|
|
" $( \".ui-button\" ).hover(\n",
|
|
" function() { $(this).addClass(\"ui-state-hover\");},\n",
|
|
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
|
|
" );\n",
|
|
"\n",
|
|
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
|
|
" nav_element.append(status_bar);\n",
|
|
" this.message = status_bar[0];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
|
|
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
|
|
" // which will in turn request a refresh of the image.\n",
|
|
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.send_message = function(type, properties) {\n",
|
|
" properties['type'] = type;\n",
|
|
" properties['figure_id'] = this.id;\n",
|
|
" this.ws.send(JSON.stringify(properties));\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.send_draw_message = function() {\n",
|
|
" if (!this.waiting) {\n",
|
|
" this.waiting = true;\n",
|
|
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
|
" var format_dropdown = fig.format_dropdown;\n",
|
|
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
|
|
" fig.ondownload(fig, format);\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
|
|
" var size = msg['size'];\n",
|
|
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
|
|
" fig._resize_canvas(size[0], size[1]);\n",
|
|
" fig.send_message(\"refresh\", {});\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
|
|
" var x0 = msg['x0'] / mpl.ratio;\n",
|
|
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
|
|
" var x1 = msg['x1'] / mpl.ratio;\n",
|
|
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
|
|
" x0 = Math.floor(x0) + 0.5;\n",
|
|
" y0 = Math.floor(y0) + 0.5;\n",
|
|
" x1 = Math.floor(x1) + 0.5;\n",
|
|
" y1 = Math.floor(y1) + 0.5;\n",
|
|
" var min_x = Math.min(x0, x1);\n",
|
|
" var min_y = Math.min(y0, y1);\n",
|
|
" var width = Math.abs(x1 - x0);\n",
|
|
" var height = Math.abs(y1 - y0);\n",
|
|
"\n",
|
|
" fig.rubberband_context.clearRect(\n",
|
|
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
|
|
"\n",
|
|
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
|
|
" // Updates the figure title.\n",
|
|
" fig.header.textContent = msg['label'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
|
|
" var cursor = msg['cursor'];\n",
|
|
" switch(cursor)\n",
|
|
" {\n",
|
|
" case 0:\n",
|
|
" cursor = 'pointer';\n",
|
|
" break;\n",
|
|
" case 1:\n",
|
|
" cursor = 'default';\n",
|
|
" break;\n",
|
|
" case 2:\n",
|
|
" cursor = 'crosshair';\n",
|
|
" break;\n",
|
|
" case 3:\n",
|
|
" cursor = 'move';\n",
|
|
" break;\n",
|
|
" }\n",
|
|
" fig.rubberband_canvas.style.cursor = cursor;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
|
|
" fig.message.textContent = msg['message'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
|
|
" // Request the server to send over a new figure.\n",
|
|
" fig.send_draw_message();\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
|
|
" fig.image_mode = msg['mode'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
|
" // Called whenever the canvas gets updated.\n",
|
|
" this.send_message(\"ack\", {});\n",
|
|
"}\n",
|
|
"\n",
|
|
"// A function to construct a web socket function for onmessage handling.\n",
|
|
"// Called in the figure constructor.\n",
|
|
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
|
|
" return function socket_on_message(evt) {\n",
|
|
" if (evt.data instanceof Blob) {\n",
|
|
" /* FIXME: We get \"Resource interpreted as Image but\n",
|
|
" * transferred with MIME type text/plain:\" errors on\n",
|
|
" * Chrome. But how to set the MIME type? It doesn't seem\n",
|
|
" * to be part of the websocket stream */\n",
|
|
" evt.data.type = \"image/png\";\n",
|
|
"\n",
|
|
" /* Free the memory for the previous frames */\n",
|
|
" if (fig.imageObj.src) {\n",
|
|
" (window.URL || window.webkitURL).revokeObjectURL(\n",
|
|
" fig.imageObj.src);\n",
|
|
" }\n",
|
|
"\n",
|
|
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
|
|
" evt.data);\n",
|
|
" fig.updated_canvas_event();\n",
|
|
" fig.waiting = false;\n",
|
|
" return;\n",
|
|
" }\n",
|
|
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
|
|
" fig.imageObj.src = evt.data;\n",
|
|
" fig.updated_canvas_event();\n",
|
|
" fig.waiting = false;\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" var msg = JSON.parse(evt.data);\n",
|
|
" var msg_type = msg['type'];\n",
|
|
"\n",
|
|
" // Call the \"handle_{type}\" callback, which takes\n",
|
|
" // the figure and JSON message as its only arguments.\n",
|
|
" try {\n",
|
|
" var callback = fig[\"handle_\" + msg_type];\n",
|
|
" } catch (e) {\n",
|
|
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" if (callback) {\n",
|
|
" try {\n",
|
|
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
|
|
" callback(fig, msg);\n",
|
|
" } catch (e) {\n",
|
|
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
|
|
" }\n",
|
|
" }\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
|
|
"mpl.findpos = function(e) {\n",
|
|
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
|
|
" var targ;\n",
|
|
" if (!e)\n",
|
|
" e = window.event;\n",
|
|
" if (e.target)\n",
|
|
" targ = e.target;\n",
|
|
" else if (e.srcElement)\n",
|
|
" targ = e.srcElement;\n",
|
|
" if (targ.nodeType == 3) // defeat Safari bug\n",
|
|
" targ = targ.parentNode;\n",
|
|
"\n",
|
|
" // jQuery normalizes the pageX and pageY\n",
|
|
" // pageX,Y are the mouse positions relative to the document\n",
|
|
" // offset() returns the position of the element relative to the document\n",
|
|
" var x = e.pageX - $(targ).offset().left;\n",
|
|
" var y = e.pageY - $(targ).offset().top;\n",
|
|
"\n",
|
|
" return {\"x\": x, \"y\": y};\n",
|
|
"};\n",
|
|
"\n",
|
|
"/*\n",
|
|
" * return a copy of an object with only non-object keys\n",
|
|
" * we need this to avoid circular references\n",
|
|
" * http://stackoverflow.com/a/24161582/3208463\n",
|
|
" */\n",
|
|
"function simpleKeys (original) {\n",
|
|
" return Object.keys(original).reduce(function (obj, key) {\n",
|
|
" if (typeof original[key] !== 'object')\n",
|
|
" obj[key] = original[key]\n",
|
|
" return obj;\n",
|
|
" }, {});\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
|
|
" var canvas_pos = mpl.findpos(event)\n",
|
|
"\n",
|
|
" if (name === 'button_press')\n",
|
|
" {\n",
|
|
" this.canvas.focus();\n",
|
|
" this.canvas_div.focus();\n",
|
|
" }\n",
|
|
"\n",
|
|
" var x = canvas_pos.x * mpl.ratio;\n",
|
|
" var y = canvas_pos.y * mpl.ratio;\n",
|
|
"\n",
|
|
" this.send_message(name, {x: x, y: y, button: event.button,\n",
|
|
" step: event.step,\n",
|
|
" guiEvent: simpleKeys(event)});\n",
|
|
"\n",
|
|
" /* This prevents the web browser from automatically changing to\n",
|
|
" * the text insertion cursor when the button is pressed. We want\n",
|
|
" * to control all of the cursor setting manually through the\n",
|
|
" * 'cursor' event from matplotlib */\n",
|
|
" event.preventDefault();\n",
|
|
" return false;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
|
" // Handle any extra behaviour associated with a key event\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.key_event = function(event, name) {\n",
|
|
"\n",
|
|
" // Prevent repeat events\n",
|
|
" if (name == 'key_press')\n",
|
|
" {\n",
|
|
" if (event.which === this._key)\n",
|
|
" return;\n",
|
|
" else\n",
|
|
" this._key = event.which;\n",
|
|
" }\n",
|
|
" if (name == 'key_release')\n",
|
|
" this._key = null;\n",
|
|
"\n",
|
|
" var value = '';\n",
|
|
" if (event.ctrlKey && event.which != 17)\n",
|
|
" value += \"ctrl+\";\n",
|
|
" if (event.altKey && event.which != 18)\n",
|
|
" value += \"alt+\";\n",
|
|
" if (event.shiftKey && event.which != 16)\n",
|
|
" value += \"shift+\";\n",
|
|
"\n",
|
|
" value += 'k';\n",
|
|
" value += event.which.toString();\n",
|
|
"\n",
|
|
" this._key_event_extra(event, name);\n",
|
|
"\n",
|
|
" this.send_message(name, {key: value,\n",
|
|
" guiEvent: simpleKeys(event)});\n",
|
|
" return false;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
|
|
" if (name == 'download') {\n",
|
|
" this.handle_save(this, null);\n",
|
|
" } else {\n",
|
|
" this.send_message(\"toolbar_button\", {name: name});\n",
|
|
" }\n",
|
|
"};\n",
|
|
"\n",
|
|
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
|
|
" this.message.textContent = tooltip;\n",
|
|
"};\n",
|
|
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
|
|
"\n",
|
|
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
|
|
"\n",
|
|
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
|
|
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
|
|
" // object with the appropriate methods. Currently this is a non binary\n",
|
|
" // socket, so there is still some room for performance tuning.\n",
|
|
" var ws = {};\n",
|
|
"\n",
|
|
" ws.close = function() {\n",
|
|
" comm.close()\n",
|
|
" };\n",
|
|
" ws.send = function(m) {\n",
|
|
" //console.log('sending', m);\n",
|
|
" comm.send(m);\n",
|
|
" };\n",
|
|
" // Register the callback with on_msg.\n",
|
|
" comm.on_msg(function(msg) {\n",
|
|
" //console.log('receiving', msg['content']['data'], msg);\n",
|
|
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
|
|
" ws.onmessage(msg['content']['data'])\n",
|
|
" });\n",
|
|
" return ws;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.mpl_figure_comm = function(comm, msg) {\n",
|
|
" // This is the function which gets called when the mpl process\n",
|
|
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
|
|
"\n",
|
|
" var id = msg.content.data.id;\n",
|
|
" // Get hold of the div created by the display call when the Comm\n",
|
|
" // socket was opened in Python.\n",
|
|
" var element = $(\"#\" + id);\n",
|
|
" var ws_proxy = comm_websocket_adapter(comm)\n",
|
|
"\n",
|
|
" function ondownload(figure, format) {\n",
|
|
" window.open(figure.imageObj.src);\n",
|
|
" }\n",
|
|
"\n",
|
|
" var fig = new mpl.figure(id, ws_proxy,\n",
|
|
" ondownload,\n",
|
|
" element.get(0));\n",
|
|
"\n",
|
|
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
|
|
" // web socket which is closed, not our websocket->open comm proxy.\n",
|
|
" ws_proxy.onopen();\n",
|
|
"\n",
|
|
" fig.parent_element = element.get(0);\n",
|
|
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
|
|
" if (!fig.cell_info) {\n",
|
|
" console.error(\"Failed to find cell for figure\", id, fig);\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" var output_index = fig.cell_info[2]\n",
|
|
" var cell = fig.cell_info[0];\n",
|
|
"\n",
|
|
"};\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
|
|
" var width = fig.canvas.width/mpl.ratio\n",
|
|
" fig.root.unbind('remove')\n",
|
|
"\n",
|
|
" // Update the output cell to use the data from the current canvas.\n",
|
|
" fig.push_to_output();\n",
|
|
" var dataURL = fig.canvas.toDataURL();\n",
|
|
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
|
|
" // the notebook keyboard shortcuts fail.\n",
|
|
" IPython.keyboard_manager.enable()\n",
|
|
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
|
|
" fig.close_ws(fig, msg);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
|
|
" fig.send_message('closing', msg);\n",
|
|
" // fig.ws.close()\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
|
|
" // Turn the data on the canvas into data in the output cell.\n",
|
|
" var width = this.canvas.width/mpl.ratio\n",
|
|
" var dataURL = this.canvas.toDataURL();\n",
|
|
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
|
" // Tell IPython that the notebook contents must change.\n",
|
|
" IPython.notebook.set_dirty(true);\n",
|
|
" this.send_message(\"ack\", {});\n",
|
|
" var fig = this;\n",
|
|
" // Wait a second, then push the new image to the DOM so\n",
|
|
" // that it is saved nicely (might be nice to debounce this).\n",
|
|
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_toolbar = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var nav_element = $('<div/>')\n",
|
|
" nav_element.attr('style', 'width: 100%');\n",
|
|
" this.root.append(nav_element);\n",
|
|
"\n",
|
|
" // Define a callback function for later on.\n",
|
|
" function toolbar_event(event) {\n",
|
|
" return fig.toolbar_button_onclick(event['data']);\n",
|
|
" }\n",
|
|
" function toolbar_mouse_event(event) {\n",
|
|
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" for(var toolbar_ind in mpl.toolbar_items){\n",
|
|
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
|
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
|
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
|
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
|
"\n",
|
|
" if (!name) { continue; };\n",
|
|
"\n",
|
|
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
|
|
" button.click(method_name, toolbar_event);\n",
|
|
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
|
" nav_element.append(button);\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Add the status bar.\n",
|
|
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
|
|
" nav_element.append(status_bar);\n",
|
|
" this.message = status_bar[0];\n",
|
|
"\n",
|
|
" // Add the close button to the window.\n",
|
|
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
|
|
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
|
|
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
|
|
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
|
|
" buttongrp.append(button);\n",
|
|
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
|
|
" titlebar.prepend(buttongrp);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._root_extra_style = function(el){\n",
|
|
" var fig = this\n",
|
|
" el.on(\"remove\", function(){\n",
|
|
"\tfig.close_ws(fig, {});\n",
|
|
" });\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
|
|
" // this is important to make the div 'focusable\n",
|
|
" el.attr('tabindex', 0)\n",
|
|
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
|
|
" // off when our div gets focus\n",
|
|
"\n",
|
|
" // location in version 3\n",
|
|
" if (IPython.notebook.keyboard_manager) {\n",
|
|
" IPython.notebook.keyboard_manager.register_events(el);\n",
|
|
" }\n",
|
|
" else {\n",
|
|
" // location in version 2\n",
|
|
" IPython.keyboard_manager.register_events(el);\n",
|
|
" }\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
|
" var manager = IPython.notebook.keyboard_manager;\n",
|
|
" if (!manager)\n",
|
|
" manager = IPython.keyboard_manager;\n",
|
|
"\n",
|
|
" // Check for shift+enter\n",
|
|
" if (event.shiftKey && event.which == 13) {\n",
|
|
" this.canvas_div.blur();\n",
|
|
" event.shiftKey = false;\n",
|
|
" // Send a \"J\" for go to next cell\n",
|
|
" event.which = 74;\n",
|
|
" event.keyCode = 74;\n",
|
|
" manager.command_mode();\n",
|
|
" manager.handle_keydown(event);\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
|
" fig.ondownload(fig, null);\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.find_output_cell = function(html_output) {\n",
|
|
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
|
|
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
|
|
" // IPython event is triggered only after the cells have been serialised, which for\n",
|
|
" // our purposes (turning an active figure into a static one), is too late.\n",
|
|
" var cells = IPython.notebook.get_cells();\n",
|
|
" var ncells = cells.length;\n",
|
|
" for (var i=0; i<ncells; i++) {\n",
|
|
" var cell = cells[i];\n",
|
|
" if (cell.cell_type === 'code'){\n",
|
|
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
|
|
" var data = cell.output_area.outputs[j];\n",
|
|
" if (data.data) {\n",
|
|
" // IPython >= 3 moved mimebundle to data attribute of output\n",
|
|
" data = data.data;\n",
|
|
" }\n",
|
|
" if (data['text/html'] == html_output) {\n",
|
|
" return [cell, data, j];\n",
|
|
" }\n",
|
|
" }\n",
|
|
" }\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"// Register the function which deals with the matplotlib target/channel.\n",
|
|
"// The kernel may be null if the page has been refreshed.\n",
|
|
"if (IPython.notebook.kernel != null) {\n",
|
|
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
|
|
"}\n"
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.Javascript object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"data": {
|
|
"text/html": [
|
|
"<img src=\"data:image/png;base64,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S0tERETo7ec7PzEAQn0PRdWqVTFo0KAc2wICAlC1alUcPHgQMTExaNSoERo1aiRs1wwL4ujoiNjYWOzevRs2Nja5DgsyYMAAXL58GXPmzJF8WBDg/fVOTk5Gw4YN8e233+LatWuiYQE098ps3boVCxcuxMWLF3H16lXMnTsXJUqUwIgRI4TX0bV6X7t2DaNGjUJMTAxu3ryJLVu2oFq1amjSpImwjz4eb42rV69CLpdj165dObbp4vH29vaGra2tMDzGxo0bUa5cOeH2DUA9TISVlZVwX1i7du1yHSaifv36OHXqFI4dO4bq1auLholISkpChQoV4Onpibi4OERFRaFEiRKSDRORV70zMjLQtm1bVK5cGbGxsaLPt+ZJxxMnTmDatGmIjY3F9evXsWrVKtjY2MDLy0tn6/3ixQv0798f0dHRuHnzJvbv34/vv/8e1atXF768Afp3vDWSk5NRokQJzJs3L8dr6OLxBoDdu3dj165duHHjBvbu3Yu6deuiYcOGQljTx893fmIAhDrxy2Qy4Smq7DQDQVtbW6NEiRLo0KEDHj58KNrn1q1baNGiBYoXL45y5cqhX79+uQ4MXK9ePZiamqJatWqSDwwMvL/ehw4deu/AoTdv3gSgflS+Xr16KFWqFEqWLIm6deti/vz5yMrKyvFaulLvO3fuoEmTJihTpgzMzMzw1VdfYcCAATnujdK3460xZMgQVKlSJccxBHTzeL87SGy1atUQGhoqGs5BM1BshQoVYGZmhqZNm+b4/Tx79gxubm4oVaoULCws4OPj88GBYm1tbTF+/PhCqWNu8qr3hwb+1oyH+eeff6Jhw4awtLSEubk5atWqhXHjxomCEqBb9U5NTYWjoyNsbGxgYmICOzs7+Pn55RjYX9+Ot0ZERASKFy+eY0B/QDePN6B+oK1atWowNTVFxYoVERQUJKqfPn6+8xMDIBEREZGBYQAkIiIiMjAMgEREREQGhgGQiIiIyMAwABIREREZGAZAIiIiIgPDAEhERERkYBgAiYiIiAwMAyARERGRgWEAJCIiIjIwDIBEREREBoYBkIgom4CAAPz888+5brO1tUV4eHghl4iIKP8xABIRvREXFwcjIyMcP3481+0KhQIuLi6FXCoiovzHAEhE9Ia3tzcaNmz43u3Ozs5wcHAovAIRERUQBkAiIgCvX79G6dKlMXHiRGGdv78/Fi1aJPzcokULODk5SVE8IqJ8xQBIRAQgISEBMpkMu3btAgBkZWXB2toaa9euFfaxtbXFr7/+KlURiYjyDQMgERGAkydPQiaT4ejRowCAnTt3QiaTYcuWLQCA6Oho0XYiIl3GAEhEBODhw4eQy+Xo2bMnzp49i2+++QatWrWCr68vzp49i7p160KhUEhdTCKifMEASET0xrhx42BhYYEKFSpg8eLFiI2NhZ2dHUqWLAlXV1ckJiZKXUQionzBAEhERERkYBgAiYiIiAwMAyARERGRgWEAJCIiIjIwDIBEREREBoYBkIiIiMjAMAASERERGRgGQCIiIiIDwwBIREREZGAYAImIiIgMDAMgERERkYFhACQiIiIyMAyARERERAbm/wG93rUh1jk5vgAAAABJRU5ErkJggg==\" width=\"640\">"
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.HTML object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"def right(gamma, alpha, beta, delta, z, f):\n",
|
|
" omega = 2*np.pi*f\n",
|
|
" denom = (omega**2 - alpha - (3/4)*beta*z**2)**2 + (delta*omega)**2\n",
|
|
" return gamma/np.sqrt(denom)\n",
|
|
"\n",
|
|
"f_resonance = 8e3\n",
|
|
"omega_resonance = 2*np.pi*f_resonance\n",
|
|
"\n",
|
|
"gamma = 1e7\n",
|
|
"alpha = omega_resonance**2\n",
|
|
"beta_mul = 1e8\n",
|
|
"delta = 200\n",
|
|
"\n",
|
|
"z, fs = np.meshgrid(\n",
|
|
" np.linspace(0, 1.2, 500),\n",
|
|
" np.linspace(7e3, 9e3, 500)\n",
|
|
")\n",
|
|
"\n",
|
|
"plt.figure()\n",
|
|
"for beta in (0.0, 1, 4):\n",
|
|
" G = right(gamma, alpha, beta*beta_mul, delta, z, fs)\n",
|
|
" plt.contour(fs, z, (z-G), [0])\n",
|
|
" plt.xlabel(\"$\\omega$\")\n",
|
|
" plt.ylabel(\"$z$\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 98,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from scipy.stats import skew\n",
|
|
"from scipy.interpolate import interp1d\n",
|
|
"\n",
|
|
"def get_pointmap(gamma, alpha, beta, delta, z, f):\n",
|
|
" fig = plt.figure()\n",
|
|
" G = right(gamma, alpha, beta, delta, z, f)\n",
|
|
" cs = plt.contour(f, z, (z-G), [0])\n",
|
|
" p = cs.collections[0].get_paths()[0]\n",
|
|
" v = p.vertices\n",
|
|
" plt.close(fig)\n",
|
|
" return v[:,0][::-1], v[:,1][::-1]\n",
|
|
"\n",
|
|
"def clip_pointmap(xs, ys):\n",
|
|
" # If softening spring, mirror the distribution across 0 and back\n",
|
|
" if skew(xs) > 0:\n",
|
|
" # Is order/direction different when softening?\n",
|
|
" raise NotImplementedError()\n",
|
|
" _xs, _ys = clip_pointmap(-1*xs, ys)\n",
|
|
" return -1*_xs, _ys\n",
|
|
" \n",
|
|
" # Find peak and continue s.t. x is monotonically increasing\n",
|
|
" indexes = []\n",
|
|
" N = len(xs)\n",
|
|
" max_x = xs[0] - 1e-9\n",
|
|
" for i in range(0, N):\n",
|
|
" if xs[i] > max_x:\n",
|
|
" indexes.append(i)\n",
|
|
" max_x = xs[i]\n",
|
|
" return xs[indexes], ys[indexes]\n",
|
|
"\n",
|
|
"def resample_pointmap(xs, ys, n):\n",
|
|
" lin = interp1d(xs, ys)\n",
|
|
" new_xs = np.linspace(xs[0], xs[-1], n)\n",
|
|
" return new_xs, lin(new_xs)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 100,
|
|
"metadata": {
|
|
"scrolled": false
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Original\n",
|
|
"1357\n",
|
|
"1357\n",
|
|
"-0.38797704316103915\n",
|
|
"695\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"application/javascript": [
|
|
"/* Put everything inside the global mpl namespace */\n",
|
|
"window.mpl = {};\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.get_websocket_type = function() {\n",
|
|
" if (typeof(WebSocket) !== 'undefined') {\n",
|
|
" return WebSocket;\n",
|
|
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
|
|
" return MozWebSocket;\n",
|
|
" } else {\n",
|
|
" alert('Your browser does not have WebSocket support.' +\n",
|
|
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
|
|
" 'Firefox 4 and 5 are also supported but you ' +\n",
|
|
" 'have to enable WebSockets in about:config.');\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
|
|
" this.id = figure_id;\n",
|
|
"\n",
|
|
" this.ws = websocket;\n",
|
|
"\n",
|
|
" this.supports_binary = (this.ws.binaryType != undefined);\n",
|
|
"\n",
|
|
" if (!this.supports_binary) {\n",
|
|
" var warnings = document.getElementById(\"mpl-warnings\");\n",
|
|
" if (warnings) {\n",
|
|
" warnings.style.display = 'block';\n",
|
|
" warnings.textContent = (\n",
|
|
" \"This browser does not support binary websocket messages. \" +\n",
|
|
" \"Performance may be slow.\");\n",
|
|
" }\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.imageObj = new Image();\n",
|
|
"\n",
|
|
" this.context = undefined;\n",
|
|
" this.message = undefined;\n",
|
|
" this.canvas = undefined;\n",
|
|
" this.rubberband_canvas = undefined;\n",
|
|
" this.rubberband_context = undefined;\n",
|
|
" this.format_dropdown = undefined;\n",
|
|
"\n",
|
|
" this.image_mode = 'full';\n",
|
|
"\n",
|
|
" this.root = $('<div/>');\n",
|
|
" this._root_extra_style(this.root)\n",
|
|
" this.root.attr('style', 'display: inline-block');\n",
|
|
"\n",
|
|
" $(parent_element).append(this.root);\n",
|
|
"\n",
|
|
" this._init_header(this);\n",
|
|
" this._init_canvas(this);\n",
|
|
" this._init_toolbar(this);\n",
|
|
"\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" this.waiting = false;\n",
|
|
"\n",
|
|
" this.ws.onopen = function () {\n",
|
|
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
|
|
" fig.send_message(\"send_image_mode\", {});\n",
|
|
" if (mpl.ratio != 1) {\n",
|
|
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
|
|
" }\n",
|
|
" fig.send_message(\"refresh\", {});\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.imageObj.onload = function() {\n",
|
|
" if (fig.image_mode == 'full') {\n",
|
|
" // Full images could contain transparency (where diff images\n",
|
|
" // almost always do), so we need to clear the canvas so that\n",
|
|
" // there is no ghosting.\n",
|
|
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
|
|
" }\n",
|
|
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
|
|
" };\n",
|
|
"\n",
|
|
" this.imageObj.onunload = function() {\n",
|
|
" fig.ws.close();\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.ws.onmessage = this._make_on_message_function(this);\n",
|
|
"\n",
|
|
" this.ondownload = ondownload;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_header = function() {\n",
|
|
" var titlebar = $(\n",
|
|
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
|
|
" 'ui-helper-clearfix\"/>');\n",
|
|
" var titletext = $(\n",
|
|
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
|
|
" 'text-align: center; padding: 3px;\"/>');\n",
|
|
" titlebar.append(titletext)\n",
|
|
" this.root.append(titlebar);\n",
|
|
" this.header = titletext[0];\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_canvas = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var canvas_div = $('<div/>');\n",
|
|
"\n",
|
|
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
|
|
"\n",
|
|
" function canvas_keyboard_event(event) {\n",
|
|
" return fig.key_event(event, event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
|
|
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
|
|
" this.canvas_div = canvas_div\n",
|
|
" this._canvas_extra_style(canvas_div)\n",
|
|
" this.root.append(canvas_div);\n",
|
|
"\n",
|
|
" var canvas = $('<canvas/>');\n",
|
|
" canvas.addClass('mpl-canvas');\n",
|
|
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
|
|
"\n",
|
|
" this.canvas = canvas[0];\n",
|
|
" this.context = canvas[0].getContext(\"2d\");\n",
|
|
"\n",
|
|
" var backingStore = this.context.backingStorePixelRatio ||\n",
|
|
"\tthis.context.webkitBackingStorePixelRatio ||\n",
|
|
"\tthis.context.mozBackingStorePixelRatio ||\n",
|
|
"\tthis.context.msBackingStorePixelRatio ||\n",
|
|
"\tthis.context.oBackingStorePixelRatio ||\n",
|
|
"\tthis.context.backingStorePixelRatio || 1;\n",
|
|
"\n",
|
|
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
|
|
"\n",
|
|
" var rubberband = $('<canvas/>');\n",
|
|
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
|
|
"\n",
|
|
" var pass_mouse_events = true;\n",
|
|
"\n",
|
|
" canvas_div.resizable({\n",
|
|
" start: function(event, ui) {\n",
|
|
" pass_mouse_events = false;\n",
|
|
" },\n",
|
|
" resize: function(event, ui) {\n",
|
|
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
|
" },\n",
|
|
" stop: function(event, ui) {\n",
|
|
" pass_mouse_events = true;\n",
|
|
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
|
" },\n",
|
|
" });\n",
|
|
"\n",
|
|
" function mouse_event_fn(event) {\n",
|
|
" if (pass_mouse_events)\n",
|
|
" return fig.mouse_event(event, event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" rubberband.mousedown('button_press', mouse_event_fn);\n",
|
|
" rubberband.mouseup('button_release', mouse_event_fn);\n",
|
|
" // Throttle sequential mouse events to 1 every 20ms.\n",
|
|
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
|
|
"\n",
|
|
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
|
|
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
|
|
"\n",
|
|
" canvas_div.on(\"wheel\", function (event) {\n",
|
|
" event = event.originalEvent;\n",
|
|
" event['data'] = 'scroll'\n",
|
|
" if (event.deltaY < 0) {\n",
|
|
" event.step = 1;\n",
|
|
" } else {\n",
|
|
" event.step = -1;\n",
|
|
" }\n",
|
|
" mouse_event_fn(event);\n",
|
|
" });\n",
|
|
"\n",
|
|
" canvas_div.append(canvas);\n",
|
|
" canvas_div.append(rubberband);\n",
|
|
"\n",
|
|
" this.rubberband = rubberband;\n",
|
|
" this.rubberband_canvas = rubberband[0];\n",
|
|
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
|
|
" this.rubberband_context.strokeStyle = \"#000000\";\n",
|
|
"\n",
|
|
" this._resize_canvas = function(width, height) {\n",
|
|
" // Keep the size of the canvas, canvas container, and rubber band\n",
|
|
" // canvas in synch.\n",
|
|
" canvas_div.css('width', width)\n",
|
|
" canvas_div.css('height', height)\n",
|
|
"\n",
|
|
" canvas.attr('width', width * mpl.ratio);\n",
|
|
" canvas.attr('height', height * mpl.ratio);\n",
|
|
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
|
|
"\n",
|
|
" rubberband.attr('width', width);\n",
|
|
" rubberband.attr('height', height);\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
|
|
" // upon first draw.\n",
|
|
" this._resize_canvas(600, 600);\n",
|
|
"\n",
|
|
" // Disable right mouse context menu.\n",
|
|
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
|
|
" return false;\n",
|
|
" });\n",
|
|
"\n",
|
|
" function set_focus () {\n",
|
|
" canvas.focus();\n",
|
|
" canvas_div.focus();\n",
|
|
" }\n",
|
|
"\n",
|
|
" window.setTimeout(set_focus, 100);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_toolbar = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var nav_element = $('<div/>')\n",
|
|
" nav_element.attr('style', 'width: 100%');\n",
|
|
" this.root.append(nav_element);\n",
|
|
"\n",
|
|
" // Define a callback function for later on.\n",
|
|
" function toolbar_event(event) {\n",
|
|
" return fig.toolbar_button_onclick(event['data']);\n",
|
|
" }\n",
|
|
" function toolbar_mouse_event(event) {\n",
|
|
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" for(var toolbar_ind in mpl.toolbar_items) {\n",
|
|
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
|
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
|
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
|
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
|
"\n",
|
|
" if (!name) {\n",
|
|
" // put a spacer in here.\n",
|
|
" continue;\n",
|
|
" }\n",
|
|
" var button = $('<button/>');\n",
|
|
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
|
|
" 'ui-button-icon-only');\n",
|
|
" button.attr('role', 'button');\n",
|
|
" button.attr('aria-disabled', 'false');\n",
|
|
" button.click(method_name, toolbar_event);\n",
|
|
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
|
"\n",
|
|
" var icon_img = $('<span/>');\n",
|
|
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
|
|
" icon_img.addClass(image);\n",
|
|
" icon_img.addClass('ui-corner-all');\n",
|
|
"\n",
|
|
" var tooltip_span = $('<span/>');\n",
|
|
" tooltip_span.addClass('ui-button-text');\n",
|
|
" tooltip_span.html(tooltip);\n",
|
|
"\n",
|
|
" button.append(icon_img);\n",
|
|
" button.append(tooltip_span);\n",
|
|
"\n",
|
|
" nav_element.append(button);\n",
|
|
" }\n",
|
|
"\n",
|
|
" var fmt_picker_span = $('<span/>');\n",
|
|
"\n",
|
|
" var fmt_picker = $('<select/>');\n",
|
|
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
|
|
" fmt_picker_span.append(fmt_picker);\n",
|
|
" nav_element.append(fmt_picker_span);\n",
|
|
" this.format_dropdown = fmt_picker[0];\n",
|
|
"\n",
|
|
" for (var ind in mpl.extensions) {\n",
|
|
" var fmt = mpl.extensions[ind];\n",
|
|
" var option = $(\n",
|
|
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
|
|
" fmt_picker.append(option)\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Add hover states to the ui-buttons\n",
|
|
" $( \".ui-button\" ).hover(\n",
|
|
" function() { $(this).addClass(\"ui-state-hover\");},\n",
|
|
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
|
|
" );\n",
|
|
"\n",
|
|
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
|
|
" nav_element.append(status_bar);\n",
|
|
" this.message = status_bar[0];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
|
|
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
|
|
" // which will in turn request a refresh of the image.\n",
|
|
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.send_message = function(type, properties) {\n",
|
|
" properties['type'] = type;\n",
|
|
" properties['figure_id'] = this.id;\n",
|
|
" this.ws.send(JSON.stringify(properties));\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.send_draw_message = function() {\n",
|
|
" if (!this.waiting) {\n",
|
|
" this.waiting = true;\n",
|
|
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
|
" var format_dropdown = fig.format_dropdown;\n",
|
|
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
|
|
" fig.ondownload(fig, format);\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
|
|
" var size = msg['size'];\n",
|
|
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
|
|
" fig._resize_canvas(size[0], size[1]);\n",
|
|
" fig.send_message(\"refresh\", {});\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
|
|
" var x0 = msg['x0'] / mpl.ratio;\n",
|
|
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
|
|
" var x1 = msg['x1'] / mpl.ratio;\n",
|
|
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
|
|
" x0 = Math.floor(x0) + 0.5;\n",
|
|
" y0 = Math.floor(y0) + 0.5;\n",
|
|
" x1 = Math.floor(x1) + 0.5;\n",
|
|
" y1 = Math.floor(y1) + 0.5;\n",
|
|
" var min_x = Math.min(x0, x1);\n",
|
|
" var min_y = Math.min(y0, y1);\n",
|
|
" var width = Math.abs(x1 - x0);\n",
|
|
" var height = Math.abs(y1 - y0);\n",
|
|
"\n",
|
|
" fig.rubberband_context.clearRect(\n",
|
|
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
|
|
"\n",
|
|
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
|
|
" // Updates the figure title.\n",
|
|
" fig.header.textContent = msg['label'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
|
|
" var cursor = msg['cursor'];\n",
|
|
" switch(cursor)\n",
|
|
" {\n",
|
|
" case 0:\n",
|
|
" cursor = 'pointer';\n",
|
|
" break;\n",
|
|
" case 1:\n",
|
|
" cursor = 'default';\n",
|
|
" break;\n",
|
|
" case 2:\n",
|
|
" cursor = 'crosshair';\n",
|
|
" break;\n",
|
|
" case 3:\n",
|
|
" cursor = 'move';\n",
|
|
" break;\n",
|
|
" }\n",
|
|
" fig.rubberband_canvas.style.cursor = cursor;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
|
|
" fig.message.textContent = msg['message'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
|
|
" // Request the server to send over a new figure.\n",
|
|
" fig.send_draw_message();\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
|
|
" fig.image_mode = msg['mode'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
|
" // Called whenever the canvas gets updated.\n",
|
|
" this.send_message(\"ack\", {});\n",
|
|
"}\n",
|
|
"\n",
|
|
"// A function to construct a web socket function for onmessage handling.\n",
|
|
"// Called in the figure constructor.\n",
|
|
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
|
|
" return function socket_on_message(evt) {\n",
|
|
" if (evt.data instanceof Blob) {\n",
|
|
" /* FIXME: We get \"Resource interpreted as Image but\n",
|
|
" * transferred with MIME type text/plain:\" errors on\n",
|
|
" * Chrome. But how to set the MIME type? It doesn't seem\n",
|
|
" * to be part of the websocket stream */\n",
|
|
" evt.data.type = \"image/png\";\n",
|
|
"\n",
|
|
" /* Free the memory for the previous frames */\n",
|
|
" if (fig.imageObj.src) {\n",
|
|
" (window.URL || window.webkitURL).revokeObjectURL(\n",
|
|
" fig.imageObj.src);\n",
|
|
" }\n",
|
|
"\n",
|
|
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
|
|
" evt.data);\n",
|
|
" fig.updated_canvas_event();\n",
|
|
" fig.waiting = false;\n",
|
|
" return;\n",
|
|
" }\n",
|
|
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
|
|
" fig.imageObj.src = evt.data;\n",
|
|
" fig.updated_canvas_event();\n",
|
|
" fig.waiting = false;\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" var msg = JSON.parse(evt.data);\n",
|
|
" var msg_type = msg['type'];\n",
|
|
"\n",
|
|
" // Call the \"handle_{type}\" callback, which takes\n",
|
|
" // the figure and JSON message as its only arguments.\n",
|
|
" try {\n",
|
|
" var callback = fig[\"handle_\" + msg_type];\n",
|
|
" } catch (e) {\n",
|
|
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" if (callback) {\n",
|
|
" try {\n",
|
|
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
|
|
" callback(fig, msg);\n",
|
|
" } catch (e) {\n",
|
|
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
|
|
" }\n",
|
|
" }\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
|
|
"mpl.findpos = function(e) {\n",
|
|
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
|
|
" var targ;\n",
|
|
" if (!e)\n",
|
|
" e = window.event;\n",
|
|
" if (e.target)\n",
|
|
" targ = e.target;\n",
|
|
" else if (e.srcElement)\n",
|
|
" targ = e.srcElement;\n",
|
|
" if (targ.nodeType == 3) // defeat Safari bug\n",
|
|
" targ = targ.parentNode;\n",
|
|
"\n",
|
|
" // jQuery normalizes the pageX and pageY\n",
|
|
" // pageX,Y are the mouse positions relative to the document\n",
|
|
" // offset() returns the position of the element relative to the document\n",
|
|
" var x = e.pageX - $(targ).offset().left;\n",
|
|
" var y = e.pageY - $(targ).offset().top;\n",
|
|
"\n",
|
|
" return {\"x\": x, \"y\": y};\n",
|
|
"};\n",
|
|
"\n",
|
|
"/*\n",
|
|
" * return a copy of an object with only non-object keys\n",
|
|
" * we need this to avoid circular references\n",
|
|
" * http://stackoverflow.com/a/24161582/3208463\n",
|
|
" */\n",
|
|
"function simpleKeys (original) {\n",
|
|
" return Object.keys(original).reduce(function (obj, key) {\n",
|
|
" if (typeof original[key] !== 'object')\n",
|
|
" obj[key] = original[key]\n",
|
|
" return obj;\n",
|
|
" }, {});\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
|
|
" var canvas_pos = mpl.findpos(event)\n",
|
|
"\n",
|
|
" if (name === 'button_press')\n",
|
|
" {\n",
|
|
" this.canvas.focus();\n",
|
|
" this.canvas_div.focus();\n",
|
|
" }\n",
|
|
"\n",
|
|
" var x = canvas_pos.x * mpl.ratio;\n",
|
|
" var y = canvas_pos.y * mpl.ratio;\n",
|
|
"\n",
|
|
" this.send_message(name, {x: x, y: y, button: event.button,\n",
|
|
" step: event.step,\n",
|
|
" guiEvent: simpleKeys(event)});\n",
|
|
"\n",
|
|
" /* This prevents the web browser from automatically changing to\n",
|
|
" * the text insertion cursor when the button is pressed. We want\n",
|
|
" * to control all of the cursor setting manually through the\n",
|
|
" * 'cursor' event from matplotlib */\n",
|
|
" event.preventDefault();\n",
|
|
" return false;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
|
" // Handle any extra behaviour associated with a key event\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.key_event = function(event, name) {\n",
|
|
"\n",
|
|
" // Prevent repeat events\n",
|
|
" if (name == 'key_press')\n",
|
|
" {\n",
|
|
" if (event.which === this._key)\n",
|
|
" return;\n",
|
|
" else\n",
|
|
" this._key = event.which;\n",
|
|
" }\n",
|
|
" if (name == 'key_release')\n",
|
|
" this._key = null;\n",
|
|
"\n",
|
|
" var value = '';\n",
|
|
" if (event.ctrlKey && event.which != 17)\n",
|
|
" value += \"ctrl+\";\n",
|
|
" if (event.altKey && event.which != 18)\n",
|
|
" value += \"alt+\";\n",
|
|
" if (event.shiftKey && event.which != 16)\n",
|
|
" value += \"shift+\";\n",
|
|
"\n",
|
|
" value += 'k';\n",
|
|
" value += event.which.toString();\n",
|
|
"\n",
|
|
" this._key_event_extra(event, name);\n",
|
|
"\n",
|
|
" this.send_message(name, {key: value,\n",
|
|
" guiEvent: simpleKeys(event)});\n",
|
|
" return false;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
|
|
" if (name == 'download') {\n",
|
|
" this.handle_save(this, null);\n",
|
|
" } else {\n",
|
|
" this.send_message(\"toolbar_button\", {name: name});\n",
|
|
" }\n",
|
|
"};\n",
|
|
"\n",
|
|
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
|
|
" this.message.textContent = tooltip;\n",
|
|
"};\n",
|
|
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
|
|
"\n",
|
|
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
|
|
"\n",
|
|
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
|
|
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
|
|
" // object with the appropriate methods. Currently this is a non binary\n",
|
|
" // socket, so there is still some room for performance tuning.\n",
|
|
" var ws = {};\n",
|
|
"\n",
|
|
" ws.close = function() {\n",
|
|
" comm.close()\n",
|
|
" };\n",
|
|
" ws.send = function(m) {\n",
|
|
" //console.log('sending', m);\n",
|
|
" comm.send(m);\n",
|
|
" };\n",
|
|
" // Register the callback with on_msg.\n",
|
|
" comm.on_msg(function(msg) {\n",
|
|
" //console.log('receiving', msg['content']['data'], msg);\n",
|
|
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
|
|
" ws.onmessage(msg['content']['data'])\n",
|
|
" });\n",
|
|
" return ws;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.mpl_figure_comm = function(comm, msg) {\n",
|
|
" // This is the function which gets called when the mpl process\n",
|
|
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
|
|
"\n",
|
|
" var id = msg.content.data.id;\n",
|
|
" // Get hold of the div created by the display call when the Comm\n",
|
|
" // socket was opened in Python.\n",
|
|
" var element = $(\"#\" + id);\n",
|
|
" var ws_proxy = comm_websocket_adapter(comm)\n",
|
|
"\n",
|
|
" function ondownload(figure, format) {\n",
|
|
" window.open(figure.imageObj.src);\n",
|
|
" }\n",
|
|
"\n",
|
|
" var fig = new mpl.figure(id, ws_proxy,\n",
|
|
" ondownload,\n",
|
|
" element.get(0));\n",
|
|
"\n",
|
|
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
|
|
" // web socket which is closed, not our websocket->open comm proxy.\n",
|
|
" ws_proxy.onopen();\n",
|
|
"\n",
|
|
" fig.parent_element = element.get(0);\n",
|
|
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
|
|
" if (!fig.cell_info) {\n",
|
|
" console.error(\"Failed to find cell for figure\", id, fig);\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" var output_index = fig.cell_info[2]\n",
|
|
" var cell = fig.cell_info[0];\n",
|
|
"\n",
|
|
"};\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
|
|
" var width = fig.canvas.width/mpl.ratio\n",
|
|
" fig.root.unbind('remove')\n",
|
|
"\n",
|
|
" // Update the output cell to use the data from the current canvas.\n",
|
|
" fig.push_to_output();\n",
|
|
" var dataURL = fig.canvas.toDataURL();\n",
|
|
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
|
|
" // the notebook keyboard shortcuts fail.\n",
|
|
" IPython.keyboard_manager.enable()\n",
|
|
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
|
|
" fig.close_ws(fig, msg);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
|
|
" fig.send_message('closing', msg);\n",
|
|
" // fig.ws.close()\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
|
|
" // Turn the data on the canvas into data in the output cell.\n",
|
|
" var width = this.canvas.width/mpl.ratio\n",
|
|
" var dataURL = this.canvas.toDataURL();\n",
|
|
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
|
" // Tell IPython that the notebook contents must change.\n",
|
|
" IPython.notebook.set_dirty(true);\n",
|
|
" this.send_message(\"ack\", {});\n",
|
|
" var fig = this;\n",
|
|
" // Wait a second, then push the new image to the DOM so\n",
|
|
" // that it is saved nicely (might be nice to debounce this).\n",
|
|
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_toolbar = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var nav_element = $('<div/>')\n",
|
|
" nav_element.attr('style', 'width: 100%');\n",
|
|
" this.root.append(nav_element);\n",
|
|
"\n",
|
|
" // Define a callback function for later on.\n",
|
|
" function toolbar_event(event) {\n",
|
|
" return fig.toolbar_button_onclick(event['data']);\n",
|
|
" }\n",
|
|
" function toolbar_mouse_event(event) {\n",
|
|
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" for(var toolbar_ind in mpl.toolbar_items){\n",
|
|
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
|
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
|
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
|
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
|
"\n",
|
|
" if (!name) { continue; };\n",
|
|
"\n",
|
|
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
|
|
" button.click(method_name, toolbar_event);\n",
|
|
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
|
" nav_element.append(button);\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Add the status bar.\n",
|
|
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
|
|
" nav_element.append(status_bar);\n",
|
|
" this.message = status_bar[0];\n",
|
|
"\n",
|
|
" // Add the close button to the window.\n",
|
|
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
|
|
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
|
|
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
|
|
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
|
|
" buttongrp.append(button);\n",
|
|
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
|
|
" titlebar.prepend(buttongrp);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._root_extra_style = function(el){\n",
|
|
" var fig = this\n",
|
|
" el.on(\"remove\", function(){\n",
|
|
"\tfig.close_ws(fig, {});\n",
|
|
" });\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
|
|
" // this is important to make the div 'focusable\n",
|
|
" el.attr('tabindex', 0)\n",
|
|
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
|
|
" // off when our div gets focus\n",
|
|
"\n",
|
|
" // location in version 3\n",
|
|
" if (IPython.notebook.keyboard_manager) {\n",
|
|
" IPython.notebook.keyboard_manager.register_events(el);\n",
|
|
" }\n",
|
|
" else {\n",
|
|
" // location in version 2\n",
|
|
" IPython.keyboard_manager.register_events(el);\n",
|
|
" }\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
|
" var manager = IPython.notebook.keyboard_manager;\n",
|
|
" if (!manager)\n",
|
|
" manager = IPython.keyboard_manager;\n",
|
|
"\n",
|
|
" // Check for shift+enter\n",
|
|
" if (event.shiftKey && event.which == 13) {\n",
|
|
" this.canvas_div.blur();\n",
|
|
" event.shiftKey = false;\n",
|
|
" // Send a \"J\" for go to next cell\n",
|
|
" event.which = 74;\n",
|
|
" event.keyCode = 74;\n",
|
|
" manager.command_mode();\n",
|
|
" manager.handle_keydown(event);\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
|
" fig.ondownload(fig, null);\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.find_output_cell = function(html_output) {\n",
|
|
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
|
|
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
|
|
" // IPython event is triggered only after the cells have been serialised, which for\n",
|
|
" // our purposes (turning an active figure into a static one), is too late.\n",
|
|
" var cells = IPython.notebook.get_cells();\n",
|
|
" var ncells = cells.length;\n",
|
|
" for (var i=0; i<ncells; i++) {\n",
|
|
" var cell = cells[i];\n",
|
|
" if (cell.cell_type === 'code'){\n",
|
|
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
|
|
" var data = cell.output_area.outputs[j];\n",
|
|
" if (data.data) {\n",
|
|
" // IPython >= 3 moved mimebundle to data attribute of output\n",
|
|
" data = data.data;\n",
|
|
" }\n",
|
|
" if (data['text/html'] == html_output) {\n",
|
|
" return [cell, data, j];\n",
|
|
" }\n",
|
|
" }\n",
|
|
" }\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"// Register the function which deals with the matplotlib target/channel.\n",
|
|
"// The kernel may be null if the page has been refreshed.\n",
|
|
"if (IPython.notebook.kernel != null) {\n",
|
|
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
|
|
"}\n"
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.Javascript object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"data": {
|
|
"text/html": [
|
|
"<img src=\"data:image/png;base64,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PffcA3d3d+j1ejz//PMWNxGOjo6Gi4sLQkND53QjaGDqjPX19VNuXtrb2wvg8mP80dHR8PDwwBVXXIGoqChs2bIFY2NjZmvNV8ap8v3yyy9ISEiAj48PXF1dsXDhQqxdu9bs3iql13C671Hg8ivoBQsWmNUEUH79Jm8+GxoaivXr18u2iTBtQOvv7w9XV1ckJiaa/VucPXsWaWlp8PDwgKenJx577LFpN6ANDAxEUVGRIjJOt6G3aT/L77//HnFxcdDpdHBzc0NERAQKCwtlDdR8ZbSWb3h4GElJSfDz84OzszNCQkKQmZlptqm+LdfQpLS0FO7u7mab6QPKriFw+SGz0NBQuLi4ICAgAE899ZQsh62fh7aKDSARERGRyrABJCIiIlIZNoBEREREKsMGkIiIiEhl2AASERERqQwbQCIiIiKVYQNIREREpDJsAImIiIhUhg0gERERkcqwASQiIiJSGTaARERERCrDBpCIiIhIZdgAEhEREakMG0AiIiIilWEDSERERKQybACJiIiIVIYNIBEREZHKsAEkIiIiUhk2gEREREQqwwaQiIiISGXYABIRERGpDBtAIiIiIpVhA0hERESkMmwAiYiIiFSGDSARERGRyrABJCIiIlIZNoBEREREKsMGkIiIiEhl2AASERERqQwbQCIiIiKVYQNIREREpDJsAImIiIhU5j+g7g5vH1agDAAAAABJRU5ErkJggg==\" width=\"640\">"
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.HTML object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Clipped\n",
|
|
"917\n",
|
|
"917\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"application/javascript": [
|
|
"/* Put everything inside the global mpl namespace */\n",
|
|
"window.mpl = {};\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.get_websocket_type = function() {\n",
|
|
" if (typeof(WebSocket) !== 'undefined') {\n",
|
|
" return WebSocket;\n",
|
|
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
|
|
" return MozWebSocket;\n",
|
|
" } else {\n",
|
|
" alert('Your browser does not have WebSocket support.' +\n",
|
|
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
|
|
" 'Firefox 4 and 5 are also supported but you ' +\n",
|
|
" 'have to enable WebSockets in about:config.');\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
|
|
" this.id = figure_id;\n",
|
|
"\n",
|
|
" this.ws = websocket;\n",
|
|
"\n",
|
|
" this.supports_binary = (this.ws.binaryType != undefined);\n",
|
|
"\n",
|
|
" if (!this.supports_binary) {\n",
|
|
" var warnings = document.getElementById(\"mpl-warnings\");\n",
|
|
" if (warnings) {\n",
|
|
" warnings.style.display = 'block';\n",
|
|
" warnings.textContent = (\n",
|
|
" \"This browser does not support binary websocket messages. \" +\n",
|
|
" \"Performance may be slow.\");\n",
|
|
" }\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.imageObj = new Image();\n",
|
|
"\n",
|
|
" this.context = undefined;\n",
|
|
" this.message = undefined;\n",
|
|
" this.canvas = undefined;\n",
|
|
" this.rubberband_canvas = undefined;\n",
|
|
" this.rubberband_context = undefined;\n",
|
|
" this.format_dropdown = undefined;\n",
|
|
"\n",
|
|
" this.image_mode = 'full';\n",
|
|
"\n",
|
|
" this.root = $('<div/>');\n",
|
|
" this._root_extra_style(this.root)\n",
|
|
" this.root.attr('style', 'display: inline-block');\n",
|
|
"\n",
|
|
" $(parent_element).append(this.root);\n",
|
|
"\n",
|
|
" this._init_header(this);\n",
|
|
" this._init_canvas(this);\n",
|
|
" this._init_toolbar(this);\n",
|
|
"\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" this.waiting = false;\n",
|
|
"\n",
|
|
" this.ws.onopen = function () {\n",
|
|
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
|
|
" fig.send_message(\"send_image_mode\", {});\n",
|
|
" if (mpl.ratio != 1) {\n",
|
|
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
|
|
" }\n",
|
|
" fig.send_message(\"refresh\", {});\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.imageObj.onload = function() {\n",
|
|
" if (fig.image_mode == 'full') {\n",
|
|
" // Full images could contain transparency (where diff images\n",
|
|
" // almost always do), so we need to clear the canvas so that\n",
|
|
" // there is no ghosting.\n",
|
|
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
|
|
" }\n",
|
|
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
|
|
" };\n",
|
|
"\n",
|
|
" this.imageObj.onunload = function() {\n",
|
|
" fig.ws.close();\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.ws.onmessage = this._make_on_message_function(this);\n",
|
|
"\n",
|
|
" this.ondownload = ondownload;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_header = function() {\n",
|
|
" var titlebar = $(\n",
|
|
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
|
|
" 'ui-helper-clearfix\"/>');\n",
|
|
" var titletext = $(\n",
|
|
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
|
|
" 'text-align: center; padding: 3px;\"/>');\n",
|
|
" titlebar.append(titletext)\n",
|
|
" this.root.append(titlebar);\n",
|
|
" this.header = titletext[0];\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_canvas = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var canvas_div = $('<div/>');\n",
|
|
"\n",
|
|
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
|
|
"\n",
|
|
" function canvas_keyboard_event(event) {\n",
|
|
" return fig.key_event(event, event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
|
|
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
|
|
" this.canvas_div = canvas_div\n",
|
|
" this._canvas_extra_style(canvas_div)\n",
|
|
" this.root.append(canvas_div);\n",
|
|
"\n",
|
|
" var canvas = $('<canvas/>');\n",
|
|
" canvas.addClass('mpl-canvas');\n",
|
|
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
|
|
"\n",
|
|
" this.canvas = canvas[0];\n",
|
|
" this.context = canvas[0].getContext(\"2d\");\n",
|
|
"\n",
|
|
" var backingStore = this.context.backingStorePixelRatio ||\n",
|
|
"\tthis.context.webkitBackingStorePixelRatio ||\n",
|
|
"\tthis.context.mozBackingStorePixelRatio ||\n",
|
|
"\tthis.context.msBackingStorePixelRatio ||\n",
|
|
"\tthis.context.oBackingStorePixelRatio ||\n",
|
|
"\tthis.context.backingStorePixelRatio || 1;\n",
|
|
"\n",
|
|
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
|
|
"\n",
|
|
" var rubberband = $('<canvas/>');\n",
|
|
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
|
|
"\n",
|
|
" var pass_mouse_events = true;\n",
|
|
"\n",
|
|
" canvas_div.resizable({\n",
|
|
" start: function(event, ui) {\n",
|
|
" pass_mouse_events = false;\n",
|
|
" },\n",
|
|
" resize: function(event, ui) {\n",
|
|
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
|
" },\n",
|
|
" stop: function(event, ui) {\n",
|
|
" pass_mouse_events = true;\n",
|
|
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
|
" },\n",
|
|
" });\n",
|
|
"\n",
|
|
" function mouse_event_fn(event) {\n",
|
|
" if (pass_mouse_events)\n",
|
|
" return fig.mouse_event(event, event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" rubberband.mousedown('button_press', mouse_event_fn);\n",
|
|
" rubberband.mouseup('button_release', mouse_event_fn);\n",
|
|
" // Throttle sequential mouse events to 1 every 20ms.\n",
|
|
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
|
|
"\n",
|
|
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
|
|
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
|
|
"\n",
|
|
" canvas_div.on(\"wheel\", function (event) {\n",
|
|
" event = event.originalEvent;\n",
|
|
" event['data'] = 'scroll'\n",
|
|
" if (event.deltaY < 0) {\n",
|
|
" event.step = 1;\n",
|
|
" } else {\n",
|
|
" event.step = -1;\n",
|
|
" }\n",
|
|
" mouse_event_fn(event);\n",
|
|
" });\n",
|
|
"\n",
|
|
" canvas_div.append(canvas);\n",
|
|
" canvas_div.append(rubberband);\n",
|
|
"\n",
|
|
" this.rubberband = rubberband;\n",
|
|
" this.rubberband_canvas = rubberband[0];\n",
|
|
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
|
|
" this.rubberband_context.strokeStyle = \"#000000\";\n",
|
|
"\n",
|
|
" this._resize_canvas = function(width, height) {\n",
|
|
" // Keep the size of the canvas, canvas container, and rubber band\n",
|
|
" // canvas in synch.\n",
|
|
" canvas_div.css('width', width)\n",
|
|
" canvas_div.css('height', height)\n",
|
|
"\n",
|
|
" canvas.attr('width', width * mpl.ratio);\n",
|
|
" canvas.attr('height', height * mpl.ratio);\n",
|
|
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
|
|
"\n",
|
|
" rubberband.attr('width', width);\n",
|
|
" rubberband.attr('height', height);\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
|
|
" // upon first draw.\n",
|
|
" this._resize_canvas(600, 600);\n",
|
|
"\n",
|
|
" // Disable right mouse context menu.\n",
|
|
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
|
|
" return false;\n",
|
|
" });\n",
|
|
"\n",
|
|
" function set_focus () {\n",
|
|
" canvas.focus();\n",
|
|
" canvas_div.focus();\n",
|
|
" }\n",
|
|
"\n",
|
|
" window.setTimeout(set_focus, 100);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_toolbar = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var nav_element = $('<div/>')\n",
|
|
" nav_element.attr('style', 'width: 100%');\n",
|
|
" this.root.append(nav_element);\n",
|
|
"\n",
|
|
" // Define a callback function for later on.\n",
|
|
" function toolbar_event(event) {\n",
|
|
" return fig.toolbar_button_onclick(event['data']);\n",
|
|
" }\n",
|
|
" function toolbar_mouse_event(event) {\n",
|
|
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" for(var toolbar_ind in mpl.toolbar_items) {\n",
|
|
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
|
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
|
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
|
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
|
"\n",
|
|
" if (!name) {\n",
|
|
" // put a spacer in here.\n",
|
|
" continue;\n",
|
|
" }\n",
|
|
" var button = $('<button/>');\n",
|
|
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
|
|
" 'ui-button-icon-only');\n",
|
|
" button.attr('role', 'button');\n",
|
|
" button.attr('aria-disabled', 'false');\n",
|
|
" button.click(method_name, toolbar_event);\n",
|
|
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
|
"\n",
|
|
" var icon_img = $('<span/>');\n",
|
|
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
|
|
" icon_img.addClass(image);\n",
|
|
" icon_img.addClass('ui-corner-all');\n",
|
|
"\n",
|
|
" var tooltip_span = $('<span/>');\n",
|
|
" tooltip_span.addClass('ui-button-text');\n",
|
|
" tooltip_span.html(tooltip);\n",
|
|
"\n",
|
|
" button.append(icon_img);\n",
|
|
" button.append(tooltip_span);\n",
|
|
"\n",
|
|
" nav_element.append(button);\n",
|
|
" }\n",
|
|
"\n",
|
|
" var fmt_picker_span = $('<span/>');\n",
|
|
"\n",
|
|
" var fmt_picker = $('<select/>');\n",
|
|
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
|
|
" fmt_picker_span.append(fmt_picker);\n",
|
|
" nav_element.append(fmt_picker_span);\n",
|
|
" this.format_dropdown = fmt_picker[0];\n",
|
|
"\n",
|
|
" for (var ind in mpl.extensions) {\n",
|
|
" var fmt = mpl.extensions[ind];\n",
|
|
" var option = $(\n",
|
|
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
|
|
" fmt_picker.append(option)\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Add hover states to the ui-buttons\n",
|
|
" $( \".ui-button\" ).hover(\n",
|
|
" function() { $(this).addClass(\"ui-state-hover\");},\n",
|
|
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
|
|
" );\n",
|
|
"\n",
|
|
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
|
|
" nav_element.append(status_bar);\n",
|
|
" this.message = status_bar[0];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
|
|
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
|
|
" // which will in turn request a refresh of the image.\n",
|
|
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.send_message = function(type, properties) {\n",
|
|
" properties['type'] = type;\n",
|
|
" properties['figure_id'] = this.id;\n",
|
|
" this.ws.send(JSON.stringify(properties));\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.send_draw_message = function() {\n",
|
|
" if (!this.waiting) {\n",
|
|
" this.waiting = true;\n",
|
|
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
|
" var format_dropdown = fig.format_dropdown;\n",
|
|
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
|
|
" fig.ondownload(fig, format);\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
|
|
" var size = msg['size'];\n",
|
|
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
|
|
" fig._resize_canvas(size[0], size[1]);\n",
|
|
" fig.send_message(\"refresh\", {});\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
|
|
" var x0 = msg['x0'] / mpl.ratio;\n",
|
|
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
|
|
" var x1 = msg['x1'] / mpl.ratio;\n",
|
|
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
|
|
" x0 = Math.floor(x0) + 0.5;\n",
|
|
" y0 = Math.floor(y0) + 0.5;\n",
|
|
" x1 = Math.floor(x1) + 0.5;\n",
|
|
" y1 = Math.floor(y1) + 0.5;\n",
|
|
" var min_x = Math.min(x0, x1);\n",
|
|
" var min_y = Math.min(y0, y1);\n",
|
|
" var width = Math.abs(x1 - x0);\n",
|
|
" var height = Math.abs(y1 - y0);\n",
|
|
"\n",
|
|
" fig.rubberband_context.clearRect(\n",
|
|
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
|
|
"\n",
|
|
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
|
|
" // Updates the figure title.\n",
|
|
" fig.header.textContent = msg['label'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
|
|
" var cursor = msg['cursor'];\n",
|
|
" switch(cursor)\n",
|
|
" {\n",
|
|
" case 0:\n",
|
|
" cursor = 'pointer';\n",
|
|
" break;\n",
|
|
" case 1:\n",
|
|
" cursor = 'default';\n",
|
|
" break;\n",
|
|
" case 2:\n",
|
|
" cursor = 'crosshair';\n",
|
|
" break;\n",
|
|
" case 3:\n",
|
|
" cursor = 'move';\n",
|
|
" break;\n",
|
|
" }\n",
|
|
" fig.rubberband_canvas.style.cursor = cursor;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
|
|
" fig.message.textContent = msg['message'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
|
|
" // Request the server to send over a new figure.\n",
|
|
" fig.send_draw_message();\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
|
|
" fig.image_mode = msg['mode'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
|
" // Called whenever the canvas gets updated.\n",
|
|
" this.send_message(\"ack\", {});\n",
|
|
"}\n",
|
|
"\n",
|
|
"// A function to construct a web socket function for onmessage handling.\n",
|
|
"// Called in the figure constructor.\n",
|
|
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
|
|
" return function socket_on_message(evt) {\n",
|
|
" if (evt.data instanceof Blob) {\n",
|
|
" /* FIXME: We get \"Resource interpreted as Image but\n",
|
|
" * transferred with MIME type text/plain:\" errors on\n",
|
|
" * Chrome. But how to set the MIME type? It doesn't seem\n",
|
|
" * to be part of the websocket stream */\n",
|
|
" evt.data.type = \"image/png\";\n",
|
|
"\n",
|
|
" /* Free the memory for the previous frames */\n",
|
|
" if (fig.imageObj.src) {\n",
|
|
" (window.URL || window.webkitURL).revokeObjectURL(\n",
|
|
" fig.imageObj.src);\n",
|
|
" }\n",
|
|
"\n",
|
|
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
|
|
" evt.data);\n",
|
|
" fig.updated_canvas_event();\n",
|
|
" fig.waiting = false;\n",
|
|
" return;\n",
|
|
" }\n",
|
|
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
|
|
" fig.imageObj.src = evt.data;\n",
|
|
" fig.updated_canvas_event();\n",
|
|
" fig.waiting = false;\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" var msg = JSON.parse(evt.data);\n",
|
|
" var msg_type = msg['type'];\n",
|
|
"\n",
|
|
" // Call the \"handle_{type}\" callback, which takes\n",
|
|
" // the figure and JSON message as its only arguments.\n",
|
|
" try {\n",
|
|
" var callback = fig[\"handle_\" + msg_type];\n",
|
|
" } catch (e) {\n",
|
|
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" if (callback) {\n",
|
|
" try {\n",
|
|
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
|
|
" callback(fig, msg);\n",
|
|
" } catch (e) {\n",
|
|
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
|
|
" }\n",
|
|
" }\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
|
|
"mpl.findpos = function(e) {\n",
|
|
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
|
|
" var targ;\n",
|
|
" if (!e)\n",
|
|
" e = window.event;\n",
|
|
" if (e.target)\n",
|
|
" targ = e.target;\n",
|
|
" else if (e.srcElement)\n",
|
|
" targ = e.srcElement;\n",
|
|
" if (targ.nodeType == 3) // defeat Safari bug\n",
|
|
" targ = targ.parentNode;\n",
|
|
"\n",
|
|
" // jQuery normalizes the pageX and pageY\n",
|
|
" // pageX,Y are the mouse positions relative to the document\n",
|
|
" // offset() returns the position of the element relative to the document\n",
|
|
" var x = e.pageX - $(targ).offset().left;\n",
|
|
" var y = e.pageY - $(targ).offset().top;\n",
|
|
"\n",
|
|
" return {\"x\": x, \"y\": y};\n",
|
|
"};\n",
|
|
"\n",
|
|
"/*\n",
|
|
" * return a copy of an object with only non-object keys\n",
|
|
" * we need this to avoid circular references\n",
|
|
" * http://stackoverflow.com/a/24161582/3208463\n",
|
|
" */\n",
|
|
"function simpleKeys (original) {\n",
|
|
" return Object.keys(original).reduce(function (obj, key) {\n",
|
|
" if (typeof original[key] !== 'object')\n",
|
|
" obj[key] = original[key]\n",
|
|
" return obj;\n",
|
|
" }, {});\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
|
|
" var canvas_pos = mpl.findpos(event)\n",
|
|
"\n",
|
|
" if (name === 'button_press')\n",
|
|
" {\n",
|
|
" this.canvas.focus();\n",
|
|
" this.canvas_div.focus();\n",
|
|
" }\n",
|
|
"\n",
|
|
" var x = canvas_pos.x * mpl.ratio;\n",
|
|
" var y = canvas_pos.y * mpl.ratio;\n",
|
|
"\n",
|
|
" this.send_message(name, {x: x, y: y, button: event.button,\n",
|
|
" step: event.step,\n",
|
|
" guiEvent: simpleKeys(event)});\n",
|
|
"\n",
|
|
" /* This prevents the web browser from automatically changing to\n",
|
|
" * the text insertion cursor when the button is pressed. We want\n",
|
|
" * to control all of the cursor setting manually through the\n",
|
|
" * 'cursor' event from matplotlib */\n",
|
|
" event.preventDefault();\n",
|
|
" return false;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
|
" // Handle any extra behaviour associated with a key event\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.key_event = function(event, name) {\n",
|
|
"\n",
|
|
" // Prevent repeat events\n",
|
|
" if (name == 'key_press')\n",
|
|
" {\n",
|
|
" if (event.which === this._key)\n",
|
|
" return;\n",
|
|
" else\n",
|
|
" this._key = event.which;\n",
|
|
" }\n",
|
|
" if (name == 'key_release')\n",
|
|
" this._key = null;\n",
|
|
"\n",
|
|
" var value = '';\n",
|
|
" if (event.ctrlKey && event.which != 17)\n",
|
|
" value += \"ctrl+\";\n",
|
|
" if (event.altKey && event.which != 18)\n",
|
|
" value += \"alt+\";\n",
|
|
" if (event.shiftKey && event.which != 16)\n",
|
|
" value += \"shift+\";\n",
|
|
"\n",
|
|
" value += 'k';\n",
|
|
" value += event.which.toString();\n",
|
|
"\n",
|
|
" this._key_event_extra(event, name);\n",
|
|
"\n",
|
|
" this.send_message(name, {key: value,\n",
|
|
" guiEvent: simpleKeys(event)});\n",
|
|
" return false;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
|
|
" if (name == 'download') {\n",
|
|
" this.handle_save(this, null);\n",
|
|
" } else {\n",
|
|
" this.send_message(\"toolbar_button\", {name: name});\n",
|
|
" }\n",
|
|
"};\n",
|
|
"\n",
|
|
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
|
|
" this.message.textContent = tooltip;\n",
|
|
"};\n",
|
|
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
|
|
"\n",
|
|
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
|
|
"\n",
|
|
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
|
|
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
|
|
" // object with the appropriate methods. Currently this is a non binary\n",
|
|
" // socket, so there is still some room for performance tuning.\n",
|
|
" var ws = {};\n",
|
|
"\n",
|
|
" ws.close = function() {\n",
|
|
" comm.close()\n",
|
|
" };\n",
|
|
" ws.send = function(m) {\n",
|
|
" //console.log('sending', m);\n",
|
|
" comm.send(m);\n",
|
|
" };\n",
|
|
" // Register the callback with on_msg.\n",
|
|
" comm.on_msg(function(msg) {\n",
|
|
" //console.log('receiving', msg['content']['data'], msg);\n",
|
|
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
|
|
" ws.onmessage(msg['content']['data'])\n",
|
|
" });\n",
|
|
" return ws;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.mpl_figure_comm = function(comm, msg) {\n",
|
|
" // This is the function which gets called when the mpl process\n",
|
|
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
|
|
"\n",
|
|
" var id = msg.content.data.id;\n",
|
|
" // Get hold of the div created by the display call when the Comm\n",
|
|
" // socket was opened in Python.\n",
|
|
" var element = $(\"#\" + id);\n",
|
|
" var ws_proxy = comm_websocket_adapter(comm)\n",
|
|
"\n",
|
|
" function ondownload(figure, format) {\n",
|
|
" window.open(figure.imageObj.src);\n",
|
|
" }\n",
|
|
"\n",
|
|
" var fig = new mpl.figure(id, ws_proxy,\n",
|
|
" ondownload,\n",
|
|
" element.get(0));\n",
|
|
"\n",
|
|
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
|
|
" // web socket which is closed, not our websocket->open comm proxy.\n",
|
|
" ws_proxy.onopen();\n",
|
|
"\n",
|
|
" fig.parent_element = element.get(0);\n",
|
|
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
|
|
" if (!fig.cell_info) {\n",
|
|
" console.error(\"Failed to find cell for figure\", id, fig);\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" var output_index = fig.cell_info[2]\n",
|
|
" var cell = fig.cell_info[0];\n",
|
|
"\n",
|
|
"};\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
|
|
" var width = fig.canvas.width/mpl.ratio\n",
|
|
" fig.root.unbind('remove')\n",
|
|
"\n",
|
|
" // Update the output cell to use the data from the current canvas.\n",
|
|
" fig.push_to_output();\n",
|
|
" var dataURL = fig.canvas.toDataURL();\n",
|
|
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
|
|
" // the notebook keyboard shortcuts fail.\n",
|
|
" IPython.keyboard_manager.enable()\n",
|
|
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
|
|
" fig.close_ws(fig, msg);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
|
|
" fig.send_message('closing', msg);\n",
|
|
" // fig.ws.close()\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
|
|
" // Turn the data on the canvas into data in the output cell.\n",
|
|
" var width = this.canvas.width/mpl.ratio\n",
|
|
" var dataURL = this.canvas.toDataURL();\n",
|
|
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
|
" // Tell IPython that the notebook contents must change.\n",
|
|
" IPython.notebook.set_dirty(true);\n",
|
|
" this.send_message(\"ack\", {});\n",
|
|
" var fig = this;\n",
|
|
" // Wait a second, then push the new image to the DOM so\n",
|
|
" // that it is saved nicely (might be nice to debounce this).\n",
|
|
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_toolbar = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var nav_element = $('<div/>')\n",
|
|
" nav_element.attr('style', 'width: 100%');\n",
|
|
" this.root.append(nav_element);\n",
|
|
"\n",
|
|
" // Define a callback function for later on.\n",
|
|
" function toolbar_event(event) {\n",
|
|
" return fig.toolbar_button_onclick(event['data']);\n",
|
|
" }\n",
|
|
" function toolbar_mouse_event(event) {\n",
|
|
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" for(var toolbar_ind in mpl.toolbar_items){\n",
|
|
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
|
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
|
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
|
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
|
"\n",
|
|
" if (!name) { continue; };\n",
|
|
"\n",
|
|
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
|
|
" button.click(method_name, toolbar_event);\n",
|
|
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
|
" nav_element.append(button);\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Add the status bar.\n",
|
|
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
|
|
" nav_element.append(status_bar);\n",
|
|
" this.message = status_bar[0];\n",
|
|
"\n",
|
|
" // Add the close button to the window.\n",
|
|
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
|
|
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
|
|
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
|
|
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
|
|
" buttongrp.append(button);\n",
|
|
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
|
|
" titlebar.prepend(buttongrp);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._root_extra_style = function(el){\n",
|
|
" var fig = this\n",
|
|
" el.on(\"remove\", function(){\n",
|
|
"\tfig.close_ws(fig, {});\n",
|
|
" });\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
|
|
" // this is important to make the div 'focusable\n",
|
|
" el.attr('tabindex', 0)\n",
|
|
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
|
|
" // off when our div gets focus\n",
|
|
"\n",
|
|
" // location in version 3\n",
|
|
" if (IPython.notebook.keyboard_manager) {\n",
|
|
" IPython.notebook.keyboard_manager.register_events(el);\n",
|
|
" }\n",
|
|
" else {\n",
|
|
" // location in version 2\n",
|
|
" IPython.keyboard_manager.register_events(el);\n",
|
|
" }\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
|
" var manager = IPython.notebook.keyboard_manager;\n",
|
|
" if (!manager)\n",
|
|
" manager = IPython.keyboard_manager;\n",
|
|
"\n",
|
|
" // Check for shift+enter\n",
|
|
" if (event.shiftKey && event.which == 13) {\n",
|
|
" this.canvas_div.blur();\n",
|
|
" event.shiftKey = false;\n",
|
|
" // Send a \"J\" for go to next cell\n",
|
|
" event.which = 74;\n",
|
|
" event.keyCode = 74;\n",
|
|
" manager.command_mode();\n",
|
|
" manager.handle_keydown(event);\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
|
" fig.ondownload(fig, null);\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.find_output_cell = function(html_output) {\n",
|
|
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
|
|
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
|
|
" // IPython event is triggered only after the cells have been serialised, which for\n",
|
|
" // our purposes (turning an active figure into a static one), is too late.\n",
|
|
" var cells = IPython.notebook.get_cells();\n",
|
|
" var ncells = cells.length;\n",
|
|
" for (var i=0; i<ncells; i++) {\n",
|
|
" var cell = cells[i];\n",
|
|
" if (cell.cell_type === 'code'){\n",
|
|
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
|
|
" var data = cell.output_area.outputs[j];\n",
|
|
" if (data.data) {\n",
|
|
" // IPython >= 3 moved mimebundle to data attribute of output\n",
|
|
" data = data.data;\n",
|
|
" }\n",
|
|
" if (data['text/html'] == html_output) {\n",
|
|
" return [cell, data, j];\n",
|
|
" }\n",
|
|
" }\n",
|
|
" }\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"// Register the function which deals with the matplotlib target/channel.\n",
|
|
"// The kernel may be null if the page has been refreshed.\n",
|
|
"if (IPython.notebook.kernel != null) {\n",
|
|
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
|
|
"}\n"
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.Javascript object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"data": {
|
|
"text/html": [
|
|
"<img src=\"data:image/png;base64,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\" width=\"640\">"
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.HTML object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Resampled\n",
|
|
"100\n",
|
|
"100\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"application/javascript": [
|
|
"/* Put everything inside the global mpl namespace */\n",
|
|
"window.mpl = {};\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.get_websocket_type = function() {\n",
|
|
" if (typeof(WebSocket) !== 'undefined') {\n",
|
|
" return WebSocket;\n",
|
|
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
|
|
" return MozWebSocket;\n",
|
|
" } else {\n",
|
|
" alert('Your browser does not have WebSocket support.' +\n",
|
|
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
|
|
" 'Firefox 4 and 5 are also supported but you ' +\n",
|
|
" 'have to enable WebSockets in about:config.');\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
|
|
" this.id = figure_id;\n",
|
|
"\n",
|
|
" this.ws = websocket;\n",
|
|
"\n",
|
|
" this.supports_binary = (this.ws.binaryType != undefined);\n",
|
|
"\n",
|
|
" if (!this.supports_binary) {\n",
|
|
" var warnings = document.getElementById(\"mpl-warnings\");\n",
|
|
" if (warnings) {\n",
|
|
" warnings.style.display = 'block';\n",
|
|
" warnings.textContent = (\n",
|
|
" \"This browser does not support binary websocket messages. \" +\n",
|
|
" \"Performance may be slow.\");\n",
|
|
" }\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.imageObj = new Image();\n",
|
|
"\n",
|
|
" this.context = undefined;\n",
|
|
" this.message = undefined;\n",
|
|
" this.canvas = undefined;\n",
|
|
" this.rubberband_canvas = undefined;\n",
|
|
" this.rubberband_context = undefined;\n",
|
|
" this.format_dropdown = undefined;\n",
|
|
"\n",
|
|
" this.image_mode = 'full';\n",
|
|
"\n",
|
|
" this.root = $('<div/>');\n",
|
|
" this._root_extra_style(this.root)\n",
|
|
" this.root.attr('style', 'display: inline-block');\n",
|
|
"\n",
|
|
" $(parent_element).append(this.root);\n",
|
|
"\n",
|
|
" this._init_header(this);\n",
|
|
" this._init_canvas(this);\n",
|
|
" this._init_toolbar(this);\n",
|
|
"\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" this.waiting = false;\n",
|
|
"\n",
|
|
" this.ws.onopen = function () {\n",
|
|
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
|
|
" fig.send_message(\"send_image_mode\", {});\n",
|
|
" if (mpl.ratio != 1) {\n",
|
|
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
|
|
" }\n",
|
|
" fig.send_message(\"refresh\", {});\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.imageObj.onload = function() {\n",
|
|
" if (fig.image_mode == 'full') {\n",
|
|
" // Full images could contain transparency (where diff images\n",
|
|
" // almost always do), so we need to clear the canvas so that\n",
|
|
" // there is no ghosting.\n",
|
|
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
|
|
" }\n",
|
|
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
|
|
" };\n",
|
|
"\n",
|
|
" this.imageObj.onunload = function() {\n",
|
|
" fig.ws.close();\n",
|
|
" }\n",
|
|
"\n",
|
|
" this.ws.onmessage = this._make_on_message_function(this);\n",
|
|
"\n",
|
|
" this.ondownload = ondownload;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_header = function() {\n",
|
|
" var titlebar = $(\n",
|
|
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
|
|
" 'ui-helper-clearfix\"/>');\n",
|
|
" var titletext = $(\n",
|
|
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
|
|
" 'text-align: center; padding: 3px;\"/>');\n",
|
|
" titlebar.append(titletext)\n",
|
|
" this.root.append(titlebar);\n",
|
|
" this.header = titletext[0];\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_canvas = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var canvas_div = $('<div/>');\n",
|
|
"\n",
|
|
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
|
|
"\n",
|
|
" function canvas_keyboard_event(event) {\n",
|
|
" return fig.key_event(event, event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
|
|
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
|
|
" this.canvas_div = canvas_div\n",
|
|
" this._canvas_extra_style(canvas_div)\n",
|
|
" this.root.append(canvas_div);\n",
|
|
"\n",
|
|
" var canvas = $('<canvas/>');\n",
|
|
" canvas.addClass('mpl-canvas');\n",
|
|
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
|
|
"\n",
|
|
" this.canvas = canvas[0];\n",
|
|
" this.context = canvas[0].getContext(\"2d\");\n",
|
|
"\n",
|
|
" var backingStore = this.context.backingStorePixelRatio ||\n",
|
|
"\tthis.context.webkitBackingStorePixelRatio ||\n",
|
|
"\tthis.context.mozBackingStorePixelRatio ||\n",
|
|
"\tthis.context.msBackingStorePixelRatio ||\n",
|
|
"\tthis.context.oBackingStorePixelRatio ||\n",
|
|
"\tthis.context.backingStorePixelRatio || 1;\n",
|
|
"\n",
|
|
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
|
|
"\n",
|
|
" var rubberband = $('<canvas/>');\n",
|
|
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
|
|
"\n",
|
|
" var pass_mouse_events = true;\n",
|
|
"\n",
|
|
" canvas_div.resizable({\n",
|
|
" start: function(event, ui) {\n",
|
|
" pass_mouse_events = false;\n",
|
|
" },\n",
|
|
" resize: function(event, ui) {\n",
|
|
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
|
" },\n",
|
|
" stop: function(event, ui) {\n",
|
|
" pass_mouse_events = true;\n",
|
|
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
|
" },\n",
|
|
" });\n",
|
|
"\n",
|
|
" function mouse_event_fn(event) {\n",
|
|
" if (pass_mouse_events)\n",
|
|
" return fig.mouse_event(event, event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" rubberband.mousedown('button_press', mouse_event_fn);\n",
|
|
" rubberband.mouseup('button_release', mouse_event_fn);\n",
|
|
" // Throttle sequential mouse events to 1 every 20ms.\n",
|
|
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
|
|
"\n",
|
|
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
|
|
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
|
|
"\n",
|
|
" canvas_div.on(\"wheel\", function (event) {\n",
|
|
" event = event.originalEvent;\n",
|
|
" event['data'] = 'scroll'\n",
|
|
" if (event.deltaY < 0) {\n",
|
|
" event.step = 1;\n",
|
|
" } else {\n",
|
|
" event.step = -1;\n",
|
|
" }\n",
|
|
" mouse_event_fn(event);\n",
|
|
" });\n",
|
|
"\n",
|
|
" canvas_div.append(canvas);\n",
|
|
" canvas_div.append(rubberband);\n",
|
|
"\n",
|
|
" this.rubberband = rubberband;\n",
|
|
" this.rubberband_canvas = rubberband[0];\n",
|
|
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
|
|
" this.rubberband_context.strokeStyle = \"#000000\";\n",
|
|
"\n",
|
|
" this._resize_canvas = function(width, height) {\n",
|
|
" // Keep the size of the canvas, canvas container, and rubber band\n",
|
|
" // canvas in synch.\n",
|
|
" canvas_div.css('width', width)\n",
|
|
" canvas_div.css('height', height)\n",
|
|
"\n",
|
|
" canvas.attr('width', width * mpl.ratio);\n",
|
|
" canvas.attr('height', height * mpl.ratio);\n",
|
|
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
|
|
"\n",
|
|
" rubberband.attr('width', width);\n",
|
|
" rubberband.attr('height', height);\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
|
|
" // upon first draw.\n",
|
|
" this._resize_canvas(600, 600);\n",
|
|
"\n",
|
|
" // Disable right mouse context menu.\n",
|
|
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
|
|
" return false;\n",
|
|
" });\n",
|
|
"\n",
|
|
" function set_focus () {\n",
|
|
" canvas.focus();\n",
|
|
" canvas_div.focus();\n",
|
|
" }\n",
|
|
"\n",
|
|
" window.setTimeout(set_focus, 100);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_toolbar = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var nav_element = $('<div/>')\n",
|
|
" nav_element.attr('style', 'width: 100%');\n",
|
|
" this.root.append(nav_element);\n",
|
|
"\n",
|
|
" // Define a callback function for later on.\n",
|
|
" function toolbar_event(event) {\n",
|
|
" return fig.toolbar_button_onclick(event['data']);\n",
|
|
" }\n",
|
|
" function toolbar_mouse_event(event) {\n",
|
|
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" for(var toolbar_ind in mpl.toolbar_items) {\n",
|
|
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
|
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
|
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
|
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
|
"\n",
|
|
" if (!name) {\n",
|
|
" // put a spacer in here.\n",
|
|
" continue;\n",
|
|
" }\n",
|
|
" var button = $('<button/>');\n",
|
|
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
|
|
" 'ui-button-icon-only');\n",
|
|
" button.attr('role', 'button');\n",
|
|
" button.attr('aria-disabled', 'false');\n",
|
|
" button.click(method_name, toolbar_event);\n",
|
|
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
|
"\n",
|
|
" var icon_img = $('<span/>');\n",
|
|
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
|
|
" icon_img.addClass(image);\n",
|
|
" icon_img.addClass('ui-corner-all');\n",
|
|
"\n",
|
|
" var tooltip_span = $('<span/>');\n",
|
|
" tooltip_span.addClass('ui-button-text');\n",
|
|
" tooltip_span.html(tooltip);\n",
|
|
"\n",
|
|
" button.append(icon_img);\n",
|
|
" button.append(tooltip_span);\n",
|
|
"\n",
|
|
" nav_element.append(button);\n",
|
|
" }\n",
|
|
"\n",
|
|
" var fmt_picker_span = $('<span/>');\n",
|
|
"\n",
|
|
" var fmt_picker = $('<select/>');\n",
|
|
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
|
|
" fmt_picker_span.append(fmt_picker);\n",
|
|
" nav_element.append(fmt_picker_span);\n",
|
|
" this.format_dropdown = fmt_picker[0];\n",
|
|
"\n",
|
|
" for (var ind in mpl.extensions) {\n",
|
|
" var fmt = mpl.extensions[ind];\n",
|
|
" var option = $(\n",
|
|
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
|
|
" fmt_picker.append(option)\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Add hover states to the ui-buttons\n",
|
|
" $( \".ui-button\" ).hover(\n",
|
|
" function() { $(this).addClass(\"ui-state-hover\");},\n",
|
|
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
|
|
" );\n",
|
|
"\n",
|
|
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
|
|
" nav_element.append(status_bar);\n",
|
|
" this.message = status_bar[0];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
|
|
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
|
|
" // which will in turn request a refresh of the image.\n",
|
|
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.send_message = function(type, properties) {\n",
|
|
" properties['type'] = type;\n",
|
|
" properties['figure_id'] = this.id;\n",
|
|
" this.ws.send(JSON.stringify(properties));\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.send_draw_message = function() {\n",
|
|
" if (!this.waiting) {\n",
|
|
" this.waiting = true;\n",
|
|
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
|
" var format_dropdown = fig.format_dropdown;\n",
|
|
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
|
|
" fig.ondownload(fig, format);\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
|
|
" var size = msg['size'];\n",
|
|
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
|
|
" fig._resize_canvas(size[0], size[1]);\n",
|
|
" fig.send_message(\"refresh\", {});\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
|
|
" var x0 = msg['x0'] / mpl.ratio;\n",
|
|
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
|
|
" var x1 = msg['x1'] / mpl.ratio;\n",
|
|
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
|
|
" x0 = Math.floor(x0) + 0.5;\n",
|
|
" y0 = Math.floor(y0) + 0.5;\n",
|
|
" x1 = Math.floor(x1) + 0.5;\n",
|
|
" y1 = Math.floor(y1) + 0.5;\n",
|
|
" var min_x = Math.min(x0, x1);\n",
|
|
" var min_y = Math.min(y0, y1);\n",
|
|
" var width = Math.abs(x1 - x0);\n",
|
|
" var height = Math.abs(y1 - y0);\n",
|
|
"\n",
|
|
" fig.rubberband_context.clearRect(\n",
|
|
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
|
|
"\n",
|
|
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
|
|
" // Updates the figure title.\n",
|
|
" fig.header.textContent = msg['label'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
|
|
" var cursor = msg['cursor'];\n",
|
|
" switch(cursor)\n",
|
|
" {\n",
|
|
" case 0:\n",
|
|
" cursor = 'pointer';\n",
|
|
" break;\n",
|
|
" case 1:\n",
|
|
" cursor = 'default';\n",
|
|
" break;\n",
|
|
" case 2:\n",
|
|
" cursor = 'crosshair';\n",
|
|
" break;\n",
|
|
" case 3:\n",
|
|
" cursor = 'move';\n",
|
|
" break;\n",
|
|
" }\n",
|
|
" fig.rubberband_canvas.style.cursor = cursor;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
|
|
" fig.message.textContent = msg['message'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
|
|
" // Request the server to send over a new figure.\n",
|
|
" fig.send_draw_message();\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
|
|
" fig.image_mode = msg['mode'];\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
|
" // Called whenever the canvas gets updated.\n",
|
|
" this.send_message(\"ack\", {});\n",
|
|
"}\n",
|
|
"\n",
|
|
"// A function to construct a web socket function for onmessage handling.\n",
|
|
"// Called in the figure constructor.\n",
|
|
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
|
|
" return function socket_on_message(evt) {\n",
|
|
" if (evt.data instanceof Blob) {\n",
|
|
" /* FIXME: We get \"Resource interpreted as Image but\n",
|
|
" * transferred with MIME type text/plain:\" errors on\n",
|
|
" * Chrome. But how to set the MIME type? It doesn't seem\n",
|
|
" * to be part of the websocket stream */\n",
|
|
" evt.data.type = \"image/png\";\n",
|
|
"\n",
|
|
" /* Free the memory for the previous frames */\n",
|
|
" if (fig.imageObj.src) {\n",
|
|
" (window.URL || window.webkitURL).revokeObjectURL(\n",
|
|
" fig.imageObj.src);\n",
|
|
" }\n",
|
|
"\n",
|
|
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
|
|
" evt.data);\n",
|
|
" fig.updated_canvas_event();\n",
|
|
" fig.waiting = false;\n",
|
|
" return;\n",
|
|
" }\n",
|
|
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
|
|
" fig.imageObj.src = evt.data;\n",
|
|
" fig.updated_canvas_event();\n",
|
|
" fig.waiting = false;\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" var msg = JSON.parse(evt.data);\n",
|
|
" var msg_type = msg['type'];\n",
|
|
"\n",
|
|
" // Call the \"handle_{type}\" callback, which takes\n",
|
|
" // the figure and JSON message as its only arguments.\n",
|
|
" try {\n",
|
|
" var callback = fig[\"handle_\" + msg_type];\n",
|
|
" } catch (e) {\n",
|
|
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" if (callback) {\n",
|
|
" try {\n",
|
|
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
|
|
" callback(fig, msg);\n",
|
|
" } catch (e) {\n",
|
|
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
|
|
" }\n",
|
|
" }\n",
|
|
" };\n",
|
|
"}\n",
|
|
"\n",
|
|
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
|
|
"mpl.findpos = function(e) {\n",
|
|
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
|
|
" var targ;\n",
|
|
" if (!e)\n",
|
|
" e = window.event;\n",
|
|
" if (e.target)\n",
|
|
" targ = e.target;\n",
|
|
" else if (e.srcElement)\n",
|
|
" targ = e.srcElement;\n",
|
|
" if (targ.nodeType == 3) // defeat Safari bug\n",
|
|
" targ = targ.parentNode;\n",
|
|
"\n",
|
|
" // jQuery normalizes the pageX and pageY\n",
|
|
" // pageX,Y are the mouse positions relative to the document\n",
|
|
" // offset() returns the position of the element relative to the document\n",
|
|
" var x = e.pageX - $(targ).offset().left;\n",
|
|
" var y = e.pageY - $(targ).offset().top;\n",
|
|
"\n",
|
|
" return {\"x\": x, \"y\": y};\n",
|
|
"};\n",
|
|
"\n",
|
|
"/*\n",
|
|
" * return a copy of an object with only non-object keys\n",
|
|
" * we need this to avoid circular references\n",
|
|
" * http://stackoverflow.com/a/24161582/3208463\n",
|
|
" */\n",
|
|
"function simpleKeys (original) {\n",
|
|
" return Object.keys(original).reduce(function (obj, key) {\n",
|
|
" if (typeof original[key] !== 'object')\n",
|
|
" obj[key] = original[key]\n",
|
|
" return obj;\n",
|
|
" }, {});\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
|
|
" var canvas_pos = mpl.findpos(event)\n",
|
|
"\n",
|
|
" if (name === 'button_press')\n",
|
|
" {\n",
|
|
" this.canvas.focus();\n",
|
|
" this.canvas_div.focus();\n",
|
|
" }\n",
|
|
"\n",
|
|
" var x = canvas_pos.x * mpl.ratio;\n",
|
|
" var y = canvas_pos.y * mpl.ratio;\n",
|
|
"\n",
|
|
" this.send_message(name, {x: x, y: y, button: event.button,\n",
|
|
" step: event.step,\n",
|
|
" guiEvent: simpleKeys(event)});\n",
|
|
"\n",
|
|
" /* This prevents the web browser from automatically changing to\n",
|
|
" * the text insertion cursor when the button is pressed. We want\n",
|
|
" * to control all of the cursor setting manually through the\n",
|
|
" * 'cursor' event from matplotlib */\n",
|
|
" event.preventDefault();\n",
|
|
" return false;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
|
" // Handle any extra behaviour associated with a key event\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.key_event = function(event, name) {\n",
|
|
"\n",
|
|
" // Prevent repeat events\n",
|
|
" if (name == 'key_press')\n",
|
|
" {\n",
|
|
" if (event.which === this._key)\n",
|
|
" return;\n",
|
|
" else\n",
|
|
" this._key = event.which;\n",
|
|
" }\n",
|
|
" if (name == 'key_release')\n",
|
|
" this._key = null;\n",
|
|
"\n",
|
|
" var value = '';\n",
|
|
" if (event.ctrlKey && event.which != 17)\n",
|
|
" value += \"ctrl+\";\n",
|
|
" if (event.altKey && event.which != 18)\n",
|
|
" value += \"alt+\";\n",
|
|
" if (event.shiftKey && event.which != 16)\n",
|
|
" value += \"shift+\";\n",
|
|
"\n",
|
|
" value += 'k';\n",
|
|
" value += event.which.toString();\n",
|
|
"\n",
|
|
" this._key_event_extra(event, name);\n",
|
|
"\n",
|
|
" this.send_message(name, {key: value,\n",
|
|
" guiEvent: simpleKeys(event)});\n",
|
|
" return false;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
|
|
" if (name == 'download') {\n",
|
|
" this.handle_save(this, null);\n",
|
|
" } else {\n",
|
|
" this.send_message(\"toolbar_button\", {name: name});\n",
|
|
" }\n",
|
|
"};\n",
|
|
"\n",
|
|
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
|
|
" this.message.textContent = tooltip;\n",
|
|
"};\n",
|
|
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
|
|
"\n",
|
|
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
|
|
"\n",
|
|
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
|
|
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
|
|
" // object with the appropriate methods. Currently this is a non binary\n",
|
|
" // socket, so there is still some room for performance tuning.\n",
|
|
" var ws = {};\n",
|
|
"\n",
|
|
" ws.close = function() {\n",
|
|
" comm.close()\n",
|
|
" };\n",
|
|
" ws.send = function(m) {\n",
|
|
" //console.log('sending', m);\n",
|
|
" comm.send(m);\n",
|
|
" };\n",
|
|
" // Register the callback with on_msg.\n",
|
|
" comm.on_msg(function(msg) {\n",
|
|
" //console.log('receiving', msg['content']['data'], msg);\n",
|
|
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
|
|
" ws.onmessage(msg['content']['data'])\n",
|
|
" });\n",
|
|
" return ws;\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.mpl_figure_comm = function(comm, msg) {\n",
|
|
" // This is the function which gets called when the mpl process\n",
|
|
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
|
|
"\n",
|
|
" var id = msg.content.data.id;\n",
|
|
" // Get hold of the div created by the display call when the Comm\n",
|
|
" // socket was opened in Python.\n",
|
|
" var element = $(\"#\" + id);\n",
|
|
" var ws_proxy = comm_websocket_adapter(comm)\n",
|
|
"\n",
|
|
" function ondownload(figure, format) {\n",
|
|
" window.open(figure.imageObj.src);\n",
|
|
" }\n",
|
|
"\n",
|
|
" var fig = new mpl.figure(id, ws_proxy,\n",
|
|
" ondownload,\n",
|
|
" element.get(0));\n",
|
|
"\n",
|
|
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
|
|
" // web socket which is closed, not our websocket->open comm proxy.\n",
|
|
" ws_proxy.onopen();\n",
|
|
"\n",
|
|
" fig.parent_element = element.get(0);\n",
|
|
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
|
|
" if (!fig.cell_info) {\n",
|
|
" console.error(\"Failed to find cell for figure\", id, fig);\n",
|
|
" return;\n",
|
|
" }\n",
|
|
"\n",
|
|
" var output_index = fig.cell_info[2]\n",
|
|
" var cell = fig.cell_info[0];\n",
|
|
"\n",
|
|
"};\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
|
|
" var width = fig.canvas.width/mpl.ratio\n",
|
|
" fig.root.unbind('remove')\n",
|
|
"\n",
|
|
" // Update the output cell to use the data from the current canvas.\n",
|
|
" fig.push_to_output();\n",
|
|
" var dataURL = fig.canvas.toDataURL();\n",
|
|
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
|
|
" // the notebook keyboard shortcuts fail.\n",
|
|
" IPython.keyboard_manager.enable()\n",
|
|
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
|
|
" fig.close_ws(fig, msg);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
|
|
" fig.send_message('closing', msg);\n",
|
|
" // fig.ws.close()\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
|
|
" // Turn the data on the canvas into data in the output cell.\n",
|
|
" var width = this.canvas.width/mpl.ratio\n",
|
|
" var dataURL = this.canvas.toDataURL();\n",
|
|
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
|
" // Tell IPython that the notebook contents must change.\n",
|
|
" IPython.notebook.set_dirty(true);\n",
|
|
" this.send_message(\"ack\", {});\n",
|
|
" var fig = this;\n",
|
|
" // Wait a second, then push the new image to the DOM so\n",
|
|
" // that it is saved nicely (might be nice to debounce this).\n",
|
|
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._init_toolbar = function() {\n",
|
|
" var fig = this;\n",
|
|
"\n",
|
|
" var nav_element = $('<div/>')\n",
|
|
" nav_element.attr('style', 'width: 100%');\n",
|
|
" this.root.append(nav_element);\n",
|
|
"\n",
|
|
" // Define a callback function for later on.\n",
|
|
" function toolbar_event(event) {\n",
|
|
" return fig.toolbar_button_onclick(event['data']);\n",
|
|
" }\n",
|
|
" function toolbar_mouse_event(event) {\n",
|
|
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
|
" }\n",
|
|
"\n",
|
|
" for(var toolbar_ind in mpl.toolbar_items){\n",
|
|
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
|
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
|
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
|
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
|
"\n",
|
|
" if (!name) { continue; };\n",
|
|
"\n",
|
|
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
|
|
" button.click(method_name, toolbar_event);\n",
|
|
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
|
" nav_element.append(button);\n",
|
|
" }\n",
|
|
"\n",
|
|
" // Add the status bar.\n",
|
|
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
|
|
" nav_element.append(status_bar);\n",
|
|
" this.message = status_bar[0];\n",
|
|
"\n",
|
|
" // Add the close button to the window.\n",
|
|
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
|
|
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
|
|
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
|
|
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
|
|
" buttongrp.append(button);\n",
|
|
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
|
|
" titlebar.prepend(buttongrp);\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._root_extra_style = function(el){\n",
|
|
" var fig = this\n",
|
|
" el.on(\"remove\", function(){\n",
|
|
"\tfig.close_ws(fig, {});\n",
|
|
" });\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
|
|
" // this is important to make the div 'focusable\n",
|
|
" el.attr('tabindex', 0)\n",
|
|
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
|
|
" // off when our div gets focus\n",
|
|
"\n",
|
|
" // location in version 3\n",
|
|
" if (IPython.notebook.keyboard_manager) {\n",
|
|
" IPython.notebook.keyboard_manager.register_events(el);\n",
|
|
" }\n",
|
|
" else {\n",
|
|
" // location in version 2\n",
|
|
" IPython.keyboard_manager.register_events(el);\n",
|
|
" }\n",
|
|
"\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
|
" var manager = IPython.notebook.keyboard_manager;\n",
|
|
" if (!manager)\n",
|
|
" manager = IPython.keyboard_manager;\n",
|
|
"\n",
|
|
" // Check for shift+enter\n",
|
|
" if (event.shiftKey && event.which == 13) {\n",
|
|
" this.canvas_div.blur();\n",
|
|
" event.shiftKey = false;\n",
|
|
" // Send a \"J\" for go to next cell\n",
|
|
" event.which = 74;\n",
|
|
" event.keyCode = 74;\n",
|
|
" manager.command_mode();\n",
|
|
" manager.handle_keydown(event);\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
|
" fig.ondownload(fig, null);\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"mpl.find_output_cell = function(html_output) {\n",
|
|
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
|
|
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
|
|
" // IPython event is triggered only after the cells have been serialised, which for\n",
|
|
" // our purposes (turning an active figure into a static one), is too late.\n",
|
|
" var cells = IPython.notebook.get_cells();\n",
|
|
" var ncells = cells.length;\n",
|
|
" for (var i=0; i<ncells; i++) {\n",
|
|
" var cell = cells[i];\n",
|
|
" if (cell.cell_type === 'code'){\n",
|
|
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
|
|
" var data = cell.output_area.outputs[j];\n",
|
|
" if (data.data) {\n",
|
|
" // IPython >= 3 moved mimebundle to data attribute of output\n",
|
|
" data = data.data;\n",
|
|
" }\n",
|
|
" if (data['text/html'] == html_output) {\n",
|
|
" return [cell, data, j];\n",
|
|
" }\n",
|
|
" }\n",
|
|
" }\n",
|
|
" }\n",
|
|
"}\n",
|
|
"\n",
|
|
"// Register the function which deals with the matplotlib target/channel.\n",
|
|
"// The kernel may be null if the page has been refreshed.\n",
|
|
"if (IPython.notebook.kernel != null) {\n",
|
|
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
|
|
"}\n"
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.Javascript object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"data": {
|
|
"text/html": [
|
|
"<img src=\"data:image/png;base64,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\" width=\"640\">"
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.HTML object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"[<matplotlib.lines.Line2D at 0x7fdccdc322e8>]"
|
|
]
|
|
},
|
|
"execution_count": 100,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"f_resonance = 8e3\n",
|
|
"omega_resonance = 2*np.pi*f_resonance\n",
|
|
"\n",
|
|
"gamma = 1e7\n",
|
|
"alpha = omega_resonance**2\n",
|
|
"beta = 2e8\n",
|
|
"delta = 200\n",
|
|
"\n",
|
|
"zs, fs = np.meshgrid(\n",
|
|
" np.linspace(0, 1.2, 500),\n",
|
|
" np.linspace(7e3, 9e3, 500)\n",
|
|
")\n",
|
|
"\n",
|
|
"plt.ioff()\n",
|
|
"xs, ys = get_pointmap(gamma, alpha, beta, delta, zs, fs)\n",
|
|
"plt.ion()\n",
|
|
"\n",
|
|
"print(\"Original\")\n",
|
|
"print(len(xs))\n",
|
|
"print(len(ys))\n",
|
|
"print(skew(xs[::-1]))\n",
|
|
"print(np.argmax(ys))\n",
|
|
"plt.figure()\n",
|
|
"plt.plot(xs, ys)\n",
|
|
"\n",
|
|
"xs, ys = clip_pointmap(xs, ys)\n",
|
|
"print(\"Clipped\")\n",
|
|
"print(len(xs))\n",
|
|
"print(len(ys))\n",
|
|
"plt.figure()\n",
|
|
"plt.plot(xs, ys)\n",
|
|
"\n",
|
|
"xs, ys = resample_pointmap(xs, ys, 100)\n",
|
|
"print(\"Resampled\")\n",
|
|
"print(len(xs))\n",
|
|
"print(len(ys))\n",
|
|
"plt.figure()\n",
|
|
"plt.plot(xs, ys)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Rough method\n",
|
|
"\n",
|
|
"#### Simulating frequency response -- no coupling\n",
|
|
"\n",
|
|
" 1. Calculate linear stiffness $\\alpha$ from resonance frequencies.\n",
|
|
" 2. Derive slope and half-width maximum by search on $\\beta$ and $\\delta$. Scale signals and apply MSE loss function.\n",
|
|
" 3. Finally, scale amplitude by the inverse of scaling factor in 2).\n",
|
|
" \n",
|
|
"Step 3) outputs ballpark parameters for frequency scan simulations. See other notebook for coupling and further optimization."
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"anaconda-cloud": {},
|
|
"kernelspec": {
|
|
"display_name": "Python 3",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.5.2"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 1
|
|
}
|