1047 lines
83 KiB
Plaintext
1047 lines
83 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"%matplotlib notebook\n",
|
|
"import numpy as np\n",
|
|
"from scipy.integrate import odeint, quad\n",
|
|
"from scipy.optimize import brentq\n",
|
|
"import matplotlib.pyplot as plt\n",
|
|
"from matplotlib import animation, rc\n",
|
|
"import seaborn as sbs\n",
|
|
"#rc('font', **{'family': 'serif', 'serif': ['Computer Modern'], 'size': 20})\n",
|
|
"#rc('text', usetex=True)\n",
|
|
"#rc('animation', html='jshtml')\n",
|
|
"#plt.rcParams[\"animation.html\"] = \"jshtml\""
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def deriv(X, t, omega, gamma, alpha, delta, beta):\n",
|
|
" \"\"\"Return the derivatives dx/dt and d2x/dt2.\"\"\"\n",
|
|
" \n",
|
|
" #dVdx = lambda x: x**3 - x\n",
|
|
" x, xdot = X\n",
|
|
" xdotdot = gamma*np.cos(omega*t) - delta*xdot + alpha*x - beta*x*x*x\n",
|
|
" return xdot, xdotdot\n",
|
|
"\n",
|
|
"def solve_duffing(tmax, dt_per_period, t_trans, x0, v0, params):\n",
|
|
" \"\"\"Solve the Duffing equation for parameters gamma, delta, omega.\n",
|
|
" \n",
|
|
" Find the numerical solution to the Duffing equation using a suitable\n",
|
|
" time grid: tmax is the maximum time (s) to integrate to; t_trans is\n",
|
|
" the initial time period of transient behaviour until the solution\n",
|
|
" settles down (if it does) to some kind of periodic motion (these data\n",
|
|
" points are dropped) and dt_per_period is the number of time samples\n",
|
|
" (of duration dt) to include per period of the driving motion (frequency\n",
|
|
" omega).\n",
|
|
" \n",
|
|
" Returns the time grid, t (after t_trans), position, x, and velocity,\n",
|
|
" xdot, dt, and step, the number of array points per period of the driving\n",
|
|
" motion.\n",
|
|
" \n",
|
|
" \"\"\"\n",
|
|
" # Time point spacings and the time grid\n",
|
|
"\n",
|
|
" omega, gamma, alpha, delta, beta = params\n",
|
|
" period = 2*np.pi/omega\n",
|
|
" dt = 2*np.pi/omega / dt_per_period\n",
|
|
" step = int(period / dt)\n",
|
|
" t = np.arange(0, tmax, dt)\n",
|
|
" # Initial conditions: x, xdot\n",
|
|
" X0 = [x0, v0]\n",
|
|
" X = odeint(deriv, X0, t, args=params)\n",
|
|
" idx = int(t_trans / dt)\n",
|
|
" return t[idx:], X[idx:], dt, step"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Set up the motion for a oscillator with initial position\n",
|
|
"# x0 and initially at rest.\n",
|
|
"x0, v0 = 1, 0.1\n",
|
|
"tmax, t_trans = 10000, 100\n",
|
|
"dt_per_period = 500\n",
|
|
"\n",
|
|
"omega = 1.0\n",
|
|
"gamma = 0.4\n",
|
|
"alpha = 1.0\n",
|
|
"delta = 0.1\n",
|
|
"beta = 1.0\n",
|
|
"\n",
|
|
"omega = 1.4\n",
|
|
"gamma, delta = 0.4, 0.1\n",
|
|
"\n",
|
|
"\n",
|
|
"params = (omega, gamma, alpha, delta, beta)\n",
|
|
"xs = []"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"(1102945,)\n",
|
|
"(1102945,)\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# Solve the equation of motion.\n",
|
|
"params = (omega, gamma, alpha, delta, 0.01)\n",
|
|
"t, X, dt, pstep = solve_duffing(tmax, dt_per_period, t_trans, x0, v0, params)\n",
|
|
"x, xdot = X.T\n",
|
|
"xs.append(x)\n",
|
|
"print(t.shape)\n",
|
|
"print(x.shape)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Solve the equation of motion.\n",
|
|
"#params = (omega, gamma, 1.2, delta, 0.1)\n",
|
|
"#t, X, dt, pstep = solve_duffing(tmax, dt_per_period, t_trans, x0, v0, params)\n",
|
|
"#x, xdot = X.T\n",
|
|
"#xs.append(x)\n",
|
|
"#print(t.shape)\n",
|
|
"#print(x.shape)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"(1102945,)\n",
|
|
"(1102945,)\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# Solve the equation of motion.\n",
|
|
"params = (omega, gamma, alpha, delta, 1.0)\n",
|
|
"t, X, dt, pstep = solve_duffing(tmax, dt_per_period, t_trans, x0, v0, params)\n",
|
|
"x, xdot = X.T\n",
|
|
"xs.append(x)\n",
|
|
"print(t.shape)\n",
|
|
"print(x.shape)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def tsanim(tss):\n",
|
|
" \"Plot an array of time series data t, x = [ti], [xi].\"\n",
|
|
" nrows = len(tss)\n",
|
|
" fig, axes = plt.subplots(nrows=nrows, ncols=1)\n",
|
|
" tsmax = lambda ts: max(len(ts[0]), len(ts[1]))\n",
|
|
" ts_maxlen = max(map(tsmax, tss))\n",
|
|
" tinit = int(0.1*ts_maxlen)\n",
|
|
" print(\"Animate time series from t=%d with %d frames\" % (tinit, ts_maxlen))\n",
|
|
"\n",
|
|
" def tsplot(ax, ts):\n",
|
|
" t, x = ts\n",
|
|
" ax.set_ylabel(r'$x [\\mathrm{m}]$')\n",
|
|
" ax.set_ylim(np.min(x), np.max(x))\n",
|
|
" line, = ax.plot(t[:tinit], x[:tinit])\n",
|
|
" return line\n",
|
|
" \n",
|
|
" lines = list(map(lambda axts: tsplot(*axts), zip(axes, tss)))\n",
|
|
" axes[-1].set_xlabel(r'$t [\\mathrm{s}]$')\n",
|
|
"\n",
|
|
" def animate(i):\n",
|
|
" \"\"\"Update the image for iteration i of the Matplotlib animation.\"\"\"\n",
|
|
" for ax, line, ts in zip(axes, lines, tss):\n",
|
|
" t, x = ts\n",
|
|
" line.set_data(t[:i+1], x[:i+1])\n",
|
|
" ax.set_xlim(t_trans, t[i])\n",
|
|
" return\n",
|
|
"\n",
|
|
" #plt.tight_layout()\n",
|
|
" \n",
|
|
" #tmp\n",
|
|
" fig.suptitle(\"Duffing oscillator, beta = 0.01 (top) and 1.0 (bottom)\")\n",
|
|
" \n",
|
|
" anim = animation.FuncAnimation(fig, animate, frames=20000, interval=1)\n",
|
|
" return anim"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"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,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAgAElEQVR4nOzdZ3gU5doH8EkUUqSEDgd1CNKkSARFAQHLEVAR9Si8B0WxF1SsxwECRHoVpIs0BRRQaTpptEjvhNBCC4EEUgghvWfn/35YdsymbrKbnd2d/++65gPDlns2z+5zz1MFEBEREZGuCFoHQERERET2xQSQiIiISGeYABIRERHpDBNAIiIiIp1hAkhERESkM0wAiYiIiHSGCSARERGRzjABJCIiItIZJoBEREREOsMEUGcyMjLwzjvvoEmTJhAEAZ999hkAICEhAS+//DLq168PQRAwZ84chIWFQRAEhIWFaRu0HaxcuRKCICA6Olo916dPH/Tp00f9d3R0NARBwMqVK+0en62YrvPIkSNah0LlmD59Otq2bQuDwWD3987Pz8fdd9+NhQsX2v29KzJs2DCIoqh1GBY5dOgQatSogStXrqjnRFHEc889p2FU9vXII4/gf//7n9ZhUBmYADoYUwVtOjw8PNCsWTP07dsXc+fORXp6ulWvP2rUKNxxxx349ttvsXr1ahw9ehQAMGTIENSqVQszZ87E6tWrERkZyQTQhgngvn37EBAQgJSUFOuDtUJ1JICBgYEICAiw2etVl5SUFLz33nto2LAhvL298fjjj+PYsWMWP//s2bPo168f7rrrLtSrVw9Dhw7FjRs3Sjxu0qRJeP7559G4cWMIglDpzyYtLQ3169fHihUr1HNZWVkICAiw23dx9uzZ+Ne//oWcnBy7vJ+lLE0ADx06hI8++ghdunTBnXfeCUGofFW3b98+9OzZE15eXmjSpAk+/fRTZGRkWPz8f//733jjjTfMztk6Abx+/ToCAgIQHh5e4v9++eUXzJkzx2bvVRUbN26Et7c34uPjNY2DSscE0MGYKugJEyZg9erVWLFiBaZMmYK+ffvCzc0NoigiIiKiyq//yCOPoGfPniXON2nSBK+99prZOYPBgJycHE1aIeytsLAQOTk5UBRFPWfLBHDmzJklEkwtVEcC+PHHH1epgrUng8GAHj164K677sK3336LBQsWoH379qhduzYuXLhQ4fNjY2PRsGFD3HfffZg7dy4mT56MevXqoXPnzsjLyzN7rCAIaNq0Kfr161elBHDOnDmoU6eOWfKVlJRUpdeqqpSUFNSsWRPLly+3y/tZ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\" width=\"640\">"
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.HTML object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Animate time series from t=110294 with 1102945 frames\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"anim = tsanim([(t, x) for x in xs])\n",
|
|
"#anim.save('animations/duffing-test.mp4', writer='ffmpeg')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 10,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"anim.save('animations/duffing-oscillator-nonlinearity-comparison.mp4', writer='ffmpeg')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
}
|
|
],
|
|
"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
|
|
}
|