mirror of
https://github.com/smyalygames/FiniteVolumeGPU.git
synced 2025-05-18 06:24:13 +02:00
871 lines
76 KiB
Plaintext
871 lines
76 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"%matplotlib notebook\n",
|
|
"\n",
|
|
"from matplotlib import rc\n",
|
|
"\n",
|
|
"import numpy as np\n",
|
|
"from matplotlib import pyplot as plt"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"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,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAgAElEQVR4XuydB3iN1x/Hv9lThthihBhJyLIptUqrVavUKKp0okbp0pqtTSmqg9qr1Krd2rNGhhkjhIggEYkMmff+n/PmT41I7s17x3tzv+/z5Ck353fO73zO9/Z8vec957VQq9Vq8CIBEiABEiABEiABEjAbAhY0gGYz1uwoCZAACZAACZAACUgEaAApBBIgARIgARIgARIwMwI0gGY24OwuCZAACZAACZAACdAAUgMkQAIkQAIkQAIkYGYEaADNbMDZXRIgARIgARIgARKgAaQGSIAESIAESIAESMDMCNAAmtmAs7skQAIkQAIkQAIkQANIDZAACZAACZAACZCAmRGgATSzAWd3SYAESIAESIAESIAGkBogARIgARIgARIgATMjQANoZgPO7pIACZAACZAACZAADSA1QAIkQAIkQAIkQAJmRoAG0MwGnN0lARIgARIgARIgARpAaoAESIAESIAESIAEzIwADaCZDTi7SwIkQAIkQAIkQAI0gNQACZAACZAACZAACZgZARpAMxtwdpcESIAESIAESIAEaACpARIgARIgARIgARIwMwI0gGY24OwuCZAACZAACZAACdAAUgMkQAIkQAIkQAIkYGYEaADNbMDZXRIgARIgARIgARKgAaQGSIAESIAESIAESMDMCNAAmtmAs7skQAIkQAIkQAIkQANIDZAACZAACZAACZCAmRGgATSzAWd3SYAESIAESIAESIAGkBogARIgARIgARIgATMjQANoZgPO7pIACZAACZAACZAADSA1QAIkQAIkQAIkQAJmRoAG0MwGnN0lARIgARIgARIgARpAaoAESIAESIAESIAEzIwADaCZDTi7SwIkQAIkQAIkQAI0gNQACZAACZAACZAACZgZARpAMxtwdpcESIAESIAESIAEaACpARIgARIgARIgARIwMwI0gGY24OwuCZAACZAACZAACdAAUgMkQAIkQAIkQAIkYGYEaADNbMDZXRIgARIgARIgARKgAaQGSIAESIAESIAESMDMCNAAmtmAs7skQAIkQAIkQAIkQANIDZAACZAACZAACZCAmRGgATSzAWd3SYAESIAESIAESIAGkBogARIgARIgARIgATMjQAOoowE/cOAApk2bhlOnTiE2NhYbNmxAx44d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5bmA52sSV1h5LuD5msQVVp4L+HZNCkDf+pa6d/5IeE6HFVaeC3i+JnGFlecCnq9JXGHluYBv16QA9K1vqXvnj4TndFhh5bmA52sSV1h5LuD5msQVVp4L+HZNCkDf+pa695ycHL3wwgt64oknFB0dXep+QqEhVp7vZayw8lzA8zWJK6w8F/B8TeLKc6vSrEkBWBo12iCAAAIIIIAAAgEsQAEYwDuPoSOAAAIIIIAAAqURoAAsjRptEEAAAQQQQACBABagAAzgncfQEUAAAQQQQACB0ghQAJZGjTYIIIAAAggggEAAC1AABsDOu/XWW7VmzRrt3btX1apVU7du3TRmzBjnCeYsRQLbtm3TqFGjtGTJEu3Zs8fx6devn5566ilFRUVBdZrAc889p4ULFzqxZXzMOzFZCgVef/11vfTSS/rll1902WWXady4cbryyivhOU3giy++cJxWrVrlWH3wwQfq1asXTsUImKc6zJ07Vxs3blSFChV0+eWXO3/HL7nkErxOE5gwYYLMj/mbbhbzGXz66ad14403YuVFAQpAL2L6qqtXX31VnTp1Up06dbR792499thjzqa+/vprX20yIPtdvHixZs+erb59+6px48ZKTU3VoEGDlJycrJdffjkg5+TLQT/zzDOqWrWqzDutJ0+eTAH4O7aJIRMzpgi84oor9M9//lOTJk3Shg0blJiY6MtdEnB9L1q0SF999ZXatGmj3r17UwCeYw92795dffr0Ubt27ZSXl+d8MV23bp0TV7GxsQG373054H//+98KDw93/o6bZdq0ac4Xje+++84pBlm8I0AB6B3HMu1l/vz5zrds84ykyMjIMt12oG3M/NEw3yS3bt0aaEMvs/FOnTpVjzzyCAXg7+IdOnRwChoTN8eXSy+91PnMmaM4LMULmPehcwTQ8+j47bffVLNmTX3++ee66qqrPG8YomtWr17dKQLvu+++EBXw/rQpAL1v6tMe9+/fr8GDBztHApctW+bTbQVD58OHD5c5Mrhy5cpgmI5P5kABWMSam5urihUras6cObrttttO/OLhhx92TpWbZM1CAeiNGNi8ebOaNGniHAVs3ry5N7oMyj7y8/Odz2P//v2dI4DNmjULynmWx6QoAMtDvRTbfPzxxzV+/HhlZWWpY8eOWrBggeLj40vRU+g02bJli3MkZ+zYsRo4cGDoTLyEM6UALAJLS0tTvXr1nNOa5hqt48vzzz/vnIb68ccfS6gbOqtzBNDzfe12u9WzZ08dOHBAX375pecNQ2hNUxibS5+ys7NVqVIlzZo1Sz169AghAd9PlQLQ98bFbmHEiBEaOXLkObeekpKipKQkZ5309HSZo3/bt2932sXFxTlFoPmjG+xLSa2Mh0nkV199tfNjrt8KlaU0VhSAZxaA5vpak3yOL+aGmRkzZjgX8LMUL0AB6HlkDBkyxLkBy5zFqV+/vucNQ2hNczR+x44dzqUp77//vvN33ByB5wig94KAAtB7liXqyRR05udcS8OGDRUTE3PGKuai/YSEBOcmkJOTVIkGEEArl9TKFH9du3aVuZbLFDdhYWEBNFu7oZbUymyNArDInFPApY8/CkDP7IYOHap58+bJ3EHdqFEjzxqxlvP0i4suusi5KYvFOwIUgN5xLNNedu7c6dyNuHTpUnXp0qVMt+3vGzPXRprir23btnr77bedO8lYzi1AAXiqj/niYOLH3AV8fDFHHcwpO24COXssUQCe+3NmTvua4s/cKPPZZ5851/+xeC5w7bXXOgc+zN8rFu8IUAB6x9FnvaxYsULmp3Pnzs4zAM3drOZ5SOaZW+vXr1d0dLTPth1oHR8/7WuK4+nTp59S/NWuXTvQpuPz8ZrTK+ayAnNXubm77vi1SObRC+aam1Bdjj8G5o033nCOsL/55puaOHGi83lr0KBBqLIUO+/Dhw/L3MxgltatW+uVV15xvoCZOzZ5ZM6pZA888IBzHduHH354yrP/zOU85rmALEUCTz75pPPMP1PwZWZm6t1339Xo0aOdG/quu+46qLwkQAHoJUhfdWMuhDV3IK5du1ZHjhxxngVonidl7m41F6uzFAmYb4b33HNPsSTm2zfLqQIDBgxwbmw4feHIcuGDoF988UXni5a5Q9M8i5NHdZz5CTJHskzBd/pi7tjkSM2pKme7XnvKlCkyn0WWIgHzqJdPP/3U+fyZAvkPf/iDzI2QFH/ejRIKQO960hsCCCCAAAIIIOD3AhSAfr+LGCACCCCAAAIIIOBdAQpA73rSGwIIIIAAAggg4PcCFIB+v4sYIAIIIIAAAggg4F0BCkDvetIbAggggAACCCDg9wIUgH6/ixggAggggAACCCDgXQEKQO960hsCCCCAAAIIIOD3AhSAfr+LGCACCCCAAAIIIOBdAQpA73rSGwIIIIAAAiEnYN5tbN4otGrVKucBzuaVd7169fKZg3kt49y5c7Vx40bnTSqXX365xowZc+ItK+YtR88884w+/vhjmden1qhRwxnPqFGjnIdLs0gUgEQBAggggAACCFgJLFq0SF999ZXatGmj3r17+7wANG/E6tOnj9q1a6e8vDw99dRTMm/O2rBhg2JjY5WamuoUgOYtK+Zd3tu3b9f999/vvFXkX//6l9Vcg6UxBWCw7EnmgQACCCCAgB8ImNfenX4EMDc313mF6cyZM3Xw4EHnFYvmiF2XLl28MuLffvtNNWvW1Oeff37W1zbOmTNH/fr1c16rGhER4ZXtBnInFICBvPcYOwIIeFXAJJEWLVrooYceknkhvVmWL1+uK6+8UgsWLND111/v1e3RGQLBKFBcAXjXXXdp27ZtGj16tOrWresUiKYgNEftmjRpYs2wefNmpx/Tnykui1smTZqkJ554QuZzzsIpYGIAAQQQOEXgo48+cq4V+vrrr9W0aVO1bt1aN910k8aNG4cUAgh4IHB6AbhlyxanONu1a5dT/B1funXrpvbt2+v555/3oNezr+J2u9WzZ08dOHBAX375ZbEr7tu3zzk9nZycrGeffdZqe8HSmCOAwbInmQcCCHhNYMiQIfrkk0+c64vWrl2rlJQUxcTEeK1/OkIgmAVOLwDNqdc777zTuTbv5CUnJ0e33367Zs+e7RwdbNSo0TlZzOdy/PjxZ6xj/n3hwoVatmyZ6tevf8bvDx065By9r1atmubPn6/IyMhg5vd4bhSAHlOxIgIIhIrA0aNHndNI5u7BlStXOheOsyCAgGcCpxeApsAzp4DXr1+v8PDwUzqpVKmSateurWPHjskcKTzXYgq4WrVqnbLK0KFDNW/ePJm7kIsrIDMzM3XDDTeoYsWKzmUcfJEr4qMA9CyeWQsBBEJIwCSqpKQkJymZa5VuueWWEJo9U0XATuD0AnDTpk3O41lMkWaup/XGYk77muLPfD4/++yzYq8jNEf+TPEXHR0tc2mHKQJZKACJAQQQQKBYAXO3orkuqVWrVs41gK+88opzYfnpRx7gQwCBIoHDhw/L3IhhFnPdrPncdO3aVdWrV1diYqJz9615TMzYsWOd36enp2vJkiXOTVc9evQoMeUDDzygWbNm6cMPPzzx7D/TiXnGn3kuoDnyd9111ykrK8spEk8+/XzBBReccSSyxAMIggYcAQyCncgUEEDAewLDhg1znhNmrv0zp6dMEqtcubJz+ogFAQSKFzBH4cxn5fSlf//+mjp1qnM03dx8MX36dO3evVvx8fHq1KmTRo4c6RSBJV3MUcbililTpjjP/jvbeEybn3/+WQ0bNizpJoNufQrAoNulTAgBBEorYJKGOWqwdOlSde7c2elmx44dzjWA5s0DgwcPLm3XtEMAAQT8SoAC0K92B4NBAAEEEEAAAQR8L0AB6HtjtoAAAggggAACCPiVAAWgX+0OBoMAAggggAACCPhegALQ98ZsAQEEEEAAAQQQ8CsBCkC/2h0MBgEEEEAAAQQQ8L0ABaDvjdkCAggggAACCCDgVwIUgH61OxgMAggggAACCCDgewEKQN8bswUEEEAAAQQQQMCvBCgA/Wp3MBgEEEAAAQQQQMD3AhSAvjdmCwgggAACCCCAgF8JUAD61e5gMAgggAACCCCAgO8FKAB9b8wWEEAAAQQQQAABvxKgAPSr3cFgEEAAAQQQQAAB3wv8HzWxEscO5HuWAAAAAElFTkSuQmCC\" width=\"640\">"
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.HTML object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<matplotlib.legend.Legend at 0xe2efbb0c88>"
|
|
]
|
|
},
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"def desingularize(x, eps):\n",
|
|
" return np.sign(x)*np.maximum(np.minimum(x*x/(np.float32(2.0)*eps)+np.float32(0.5)*eps, eps), abs(x))\n",
|
|
"\n",
|
|
"eps = np.float32(2.0e-22)\n",
|
|
"x = np.linspace(-1.5*eps, 1.5*eps, 500).astype(np.float32)\n",
|
|
"y = desingularize(x, eps)\n",
|
|
"\n",
|
|
"plt.figure()\n",
|
|
"plt.plot(x, x, label='orig')\n",
|
|
"plt.plot(x, y, label='limited')\n",
|
|
"plt.xlabel('x')\n",
|
|
"plt.ylabel('Desingularized')\n",
|
|
"plt.legend()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
}
|
|
],
|
|
"metadata": {
|
|
"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.6.5"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
}
|