{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "In this post, we're going to produce a couple of animations related to typical sound sources encountered in the study of acoustics: monopole, dipole and quadrupole sources (using `holoviews` with the `matplotlib` backend)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We start by creating an $(x, y)$ grid of space." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": [ "function HoloViewsWidget() {\n", "}\n", "\n", "HoloViewsWidget.prototype.init_slider = function(init_val){\n", " if(this.load_json) {\n", " this.from_json()\n", " } else {\n", " this.update_cache();\n", " }\n", "}\n", "\n", "HoloViewsWidget.prototype.populate_cache = function(idx){\n", " this.cache[idx].innerHTML = this.frames[idx];\n", " if (this.embed) {\n", " delete this.frames[idx];\n", " }\n", "}\n", "\n", "HoloViewsWidget.prototype.process_error = function(msg){\n", "}\n", "\n", "HoloViewsWidget.prototype.from_json = function() {\n", " var data_url = this.json_path + this.id + '.json';\n", " $.getJSON(data_url, $.proxy(function(json_data) {\n", " this.frames = json_data;\n", " this.update_cache();\n", " this.update(0);\n", " }, this));\n", "}\n", "\n", "HoloViewsWidget.prototype.dynamic_update = function(current){\n", " if (current === undefined) {\n", " return\n", " }\n", " this.current = current;\n", " if (this.comm) {\n", " var msg = {comm_id: this.id+'_client', content: current}\n", " this.comm.send(msg);\n", " }\n", "}\n", "\n", "HoloViewsWidget.prototype.update_cache = function(force){\n", " var frame_len = Object.keys(this.frames).length;\n", " for (var i=0; i 0) {\n", " that.time = Date.now();\n", " that.dynamic_update(that.queue[that.queue.length-1]);\n", " that.queue = [];\n", " } else {\n", " that.wait = false;\n", " }\n", " if ((msg.msg_type == \"Ready\") && msg.content) {\n", " console.log(\"Python callback returned following output:\", msg.content);\n", " } else if (msg.msg_type == \"Error\") {\n", " console.log(\"Python failed with the following traceback:\", msg.traceback)\n", " }\n", " }\n", " var comm = HoloViews.comm_manager.get_client_comm(this.plot_id, this.id+'_client', ack_callback);\n", " return comm\n", " }\n", "}\n", "\n", "HoloViewsWidget.prototype.msg_handler = function(msg) {\n", " var metadata = msg.metadata;\n", " if ((metadata.msg_type == \"Ready\")) {\n", " if (metadata.content) {\n", " console.log(\"Python callback returned following output:\", metadata.content);\n", " }\n", "\treturn;\n", " } else if (metadata.msg_type == \"Error\") {\n", " console.log(\"Python failed with the following traceback:\", metadata.traceback)\n", " return\n", " }\n", " this.process_msg(msg)\n", "}\n", "\n", "HoloViewsWidget.prototype.process_msg = function(msg) {\n", "}\n", "\n", "function SelectionWidget(frames, id, slider_ids, keyMap, dim_vals, notFound, load_json, mode, cached, json_path, dynamic, plot_id){\n", " this.frames = frames;\n", " this.id = id;\n", " this.plot_id = plot_id;\n", " this.slider_ids = slider_ids;\n", " this.keyMap = keyMap\n", " this.current_frame = 0;\n", " this.current_vals = dim_vals;\n", " this.load_json = load_json;\n", " this.mode = mode;\n", " this.notFound = notFound;\n", " this.cached = cached;\n", " this.dynamic = dynamic;\n", " this.cache = {};\n", " this.json_path = json_path;\n", " this.init_slider(this.current_vals[0]);\n", " this.queue = [];\n", " this.wait = false;\n", " if (!this.cached || this.dynamic) {\n", " this.comm = this.init_comms();\n", " }\n", "}\n", "\n", "SelectionWidget.prototype = new HoloViewsWidget;\n", "\n", "\n", "SelectionWidget.prototype.get_key = function(current_vals) {\n", " var key = \"(\";\n", " for (var i=0; i Date.now()))) {\n", " this.queue.push(key);\n", " return\n", " }\n", " this.queue = [];\n", " this.time = Date.now();\n", " this.current_frame = key;\n", " this.wait = true;\n", " this.dynamic_update(key)\n", " } else if (key !== undefined) {\n", " this.update(key)\n", " }\n", "}\n", "\n", "\n", "/* Define the ScrubberWidget class */\n", "function ScrubberWidget(frames, num_frames, id, interval, load_json, mode, cached, json_path, dynamic, plot_id){\n", " this.slider_id = \"_anim_slider\" + id;\n", " this.loop_select_id = \"_anim_loop_select\" + id;\n", " this.id = id;\n", " this.plot_id = plot_id;\n", " this.interval = interval;\n", " this.current_frame = 0;\n", " this.direction = 0;\n", " this.dynamic = dynamic;\n", " this.timer = null;\n", " this.load_json = load_json;\n", " this.mode = mode;\n", " this.cached = cached;\n", " this.frames = frames;\n", " this.cache = {};\n", " this.length = num_frames;\n", " this.json_path = json_path;\n", " document.getElementById(this.slider_id).max = this.length - 1;\n", " this.init_slider(0);\n", " this.wait = false;\n", " this.queue = [];\n", " if (!this.cached || this.dynamic) {\n", " this.comm = this.init_comms()\n", " }\n", "}\n", "\n", "ScrubberWidget.prototype = new HoloViewsWidget;\n", "\n", "ScrubberWidget.prototype.set_frame = function(frame){\n", " this.current_frame = frame;\n", " var widget = document.getElementById(this.slider_id);\n", " if (widget === null) {\n", " this.pause_animation();\n", " return\n", " }\n", " widget.value = this.current_frame;\n", " if (this.dynamic || !this.cached) {\n", " if ((this.time !== undefined) && ((this.wait) && ((this.time + 10000) > Date.now()))) {\n", " this.queue.push(frame);\n", " return\n", " }\n", " this.queue = [];\n", " this.time = Date.now();\n", " this.wait = true;\n", " this.dynamic_update(frame)\n", " } else {\n", " this.update(frame)\n", " }\n", "}\n", "\n", "ScrubberWidget.prototype.get_loop_state = function(){\n", " var button_group = document[this.loop_select_id].state;\n", " for (var i = 0; i < button_group.length; i++) {\n", " var button = button_group[i];\n", " if (button.checked) {\n", " return button.value;\n", " }\n", " }\n", " return undefined;\n", "}\n", "\n", "\n", "ScrubberWidget.prototype.next_frame = function() {\n", " this.set_frame(Math.min(this.length - 1, this.current_frame + 1));\n", "}\n", "\n", "ScrubberWidget.prototype.previous_frame = function() {\n", " this.set_frame(Math.max(0, this.current_frame - 1));\n", "}\n", "\n", "ScrubberWidget.prototype.first_frame = function() {\n", " this.set_frame(0);\n", "}\n", "\n", "ScrubberWidget.prototype.last_frame = function() {\n", " this.set_frame(this.length - 1);\n", "}\n", "\n", "ScrubberWidget.prototype.slower = function() {\n", " this.interval /= 0.7;\n", " if(this.direction > 0){this.play_animation();}\n", " else if(this.direction < 0){this.reverse_animation();}\n", "}\n", "\n", "ScrubberWidget.prototype.faster = function() {\n", " this.interval *= 0.7;\n", " if(this.direction > 0){this.play_animation();}\n", " else if(this.direction < 0){this.reverse_animation();}\n", "}\n", "\n", "ScrubberWidget.prototype.anim_step_forward = function() {\n", " if(this.current_frame < this.length - 1){\n", " this.next_frame();\n", " }else{\n", " var loop_state = this.get_loop_state();\n", " if(loop_state == \"loop\"){\n", " this.first_frame();\n", " }else if(loop_state == \"reflect\"){\n", " this.last_frame();\n", " this.reverse_animation();\n", " }else{\n", " this.pause_animation();\n", " this.last_frame();\n", " }\n", " }\n", "}\n", "\n", "ScrubberWidget.prototype.anim_step_reverse = function() {\n", " if(this.current_frame > 0){\n", " this.previous_frame();\n", " } else {\n", " var loop_state = this.get_loop_state();\n", " if(loop_state == \"loop\"){\n", " this.last_frame();\n", " }else if(loop_state == \"reflect\"){\n", " this.first_frame();\n", " this.play_animation();\n", " }else{\n", " this.pause_animation();\n", " this.first_frame();\n", " }\n", " }\n", "}\n", "\n", "ScrubberWidget.prototype.pause_animation = function() {\n", " this.direction = 0;\n", " if (this.timer){\n", " clearInterval(this.timer);\n", " this.timer = null;\n", " }\n", "}\n", "\n", "ScrubberWidget.prototype.play_animation = function() {\n", " this.pause_animation();\n", " this.direction = 1;\n", " var t = this;\n", " if (!this.timer) this.timer = setInterval(function(){t.anim_step_forward();}, this.interval);\n", "}\n", "\n", "ScrubberWidget.prototype.reverse_animation = function() {\n", " this.pause_animation();\n", " this.direction = -1;\n", " var t = this;\n", " if (!this.timer) this.timer = setInterval(function(){t.anim_step_reverse();}, this.interval);\n", "}\n", "\n", "function extend(destination, source) {\n", " for (var k in source) {\n", " if (source.hasOwnProperty(k)) {\n", " destination[k] = source[k];\n", " }\n", " }\n", " return destination;\n", "}\n", "\n", "function update_widget(widget, values) {\n", " if (widget.hasClass(\"ui-slider\")) {\n", " widget.slider('option', {\n", " min: 0,\n", " max: values.length-1,\n", " dim_vals: values,\n", " value: 0,\n", " dim_labels: values\n", " })\n", " widget.slider('option', 'slide').call(widget, event, {value: 0})\n", " } else {\n", " widget.empty();\n", " for (var i=0; i\", {\n", " value: i,\n", " text: values[i]\n", " }))\n", " };\n", " widget.data('values', values);\n", " widget.data('value', 0);\n", " widget.trigger(\"change\");\n", " };\n", "}\n", "\n", "function init_slider(id, plot_id, dim, values, next_vals, labels, dynamic, step, value, next_dim,\n", " dim_idx, delay, jQueryUI_CDN, UNDERSCORE_CDN) {\n", " // Slider JS Block START\n", " function loadcssfile(filename){\n", " var fileref=document.createElement(\"link\")\n", " fileref.setAttribute(\"rel\", \"stylesheet\")\n", " fileref.setAttribute(\"type\", \"text/css\")\n", " fileref.setAttribute(\"href\", filename)\n", " document.getElementsByTagName(\"head\")[0].appendChild(fileref)\n", " }\n", " loadcssfile(\"https://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css\");\n", " /* Check if jQuery and jQueryUI have been loaded\n", " otherwise load with require.js */\n", " var jQuery = window.jQuery,\n", " // check for old versions of jQuery\n", " oldjQuery = jQuery && !!jQuery.fn.jquery.match(/^1\\.[0-4](\\.|$)/),\n", " jquery_path = '',\n", " paths = {},\n", " noConflict;\n", " var jQueryUI = jQuery.ui;\n", " // check for jQuery\n", " if (!jQuery || oldjQuery) {\n", " // load if it's not available or doesn't meet min standards\n", " paths.jQuery = jQuery;\n", " noConflict = !!oldjQuery;\n", " } else {\n", " // register the current jQuery\n", " define('jquery', [], function() { return jQuery; });\n", " }\n", " if (!jQueryUI) {\n", " paths.jQueryUI = jQueryUI_CDN.slice(null, -3);\n", " } else {\n", " define('jQueryUI', [], function() { return jQuery.ui; });\n", " }\n", " paths.underscore = UNDERSCORE_CDN.slice(null, -3);\n", " var jquery_require = {\n", " paths: paths,\n", " shim: {\n", " \"jQueryUI\": {\n", " exports:\"$\",\n", " deps: ['jquery']\n", " },\n", " \"underscore\": {\n", " exports: '_'\n", " }\n", " }\n", " }\n", " require.config(jquery_require);\n", " require([\"jQueryUI\", \"underscore\"], function(jUI, _){\n", " if (noConflict) $.noConflict(true);\n", " var vals = values;\n", " if (dynamic && vals.constructor === Array) {\n", " var default_value = parseFloat(value);\n", " var min = parseFloat(vals[0]);\n", " var max = parseFloat(vals[vals.length-1]);\n", " var wstep = step;\n", " var wlabels = [default_value];\n", " var init_label = default_value;\n", " } else {\n", " var min = 0;\n", " if (dynamic) {\n", " var max = Object.keys(vals).length - 1;\n", " var init_label = labels[value];\n", " var default_value = values[value];\n", " } else {\n", " var max = vals.length - 1;\n", " var init_label = labels[value];\n", " var default_value = value;\n", " }\n", " var wstep = 1;\n", " var wlabels = labels;\n", " }\n", " function adjustFontSize(text) {\n", " var width_ratio = (text.parent().width()/8)/text.val().length;\n", " var size = Math.min(0.9, Math.max(0.6, width_ratio))+'em';\n", " text.css('font-size', size);\n", " }\n", " var slider = $('#_anim_widget'+id+'_'+dim);\n", " slider.slider({\n", " animate: \"fast\",\n", " min: min,\n", " max: max,\n", " step: wstep,\n", " value: default_value,\n", " dim_vals: vals,\n", " dim_labels: wlabels,\n", " next_vals: next_vals,\n", " slide: function(event, ui) {\n", " var vals = slider.slider(\"option\", \"dim_vals\");\n", " var next_vals = slider.slider(\"option\", \"next_vals\");\n", " var dlabels = slider.slider(\"option\", \"dim_labels\");\n", " if (dynamic) {\n", " var dim_val = ui.value;\n", " if (vals.constructor === Array) {\n", " var label = ui.value;\n", " } else {\n", " var label = dlabels[ui.value];\n", " }\n", " } else {\n", " var dim_val = vals[ui.value];\n", " var label = dlabels[ui.value];\n", " }\n", " var text = $('#textInput'+id+'_'+dim);\n", " text.val(label);\n", " adjustFontSize(text);\n", " HoloViews.index[plot_id].set_frame(dim_val, dim_idx);\n", " if (Object.keys(next_vals).length > 0) {\n", " var new_vals = next_vals[dim_val];\n", " var next_widget = $('#_anim_widget'+id+'_'+next_dim);\n", " update_widget(next_widget, new_vals);\n", " }\n", " }\n", " });\n", " slider.keypress(function(event) {\n", " if (event.which == 80 || event.which == 112) {\n", " var start = slider.slider(\"option\", \"value\");\n", " var stop = slider.slider(\"option\", \"max\");\n", " for (var i=start; i<=stop; i++) {\n", " var delay = i*delay;\n", " $.proxy(function doSetTimeout(i) { setTimeout($.proxy(function() {\n", " var val = {value:i};\n", " slider.slider('value',i);\n", " slider.slider(\"option\", \"slide\")(null, val);\n", " }, slider), delay);}, slider)(i);\n", " }\n", " }\n", " if (event.which == 82 || event.which == 114) {\n", " var start = slider.slider(\"option\", \"value\");\n", " var stop = slider.slider(\"option\", \"min\");\n", " var count = 0;\n", " for (var i=start; i>=stop; i--) {\n", " var delay = count*delay;\n", " count = count + 1;\n", " $.proxy(function doSetTimeout(i) { setTimeout($.proxy(function() {\n", " var val = {value:i};\n", " slider.slider('value',i);\n", " slider.slider(\"option\", \"slide\")(null, val);\n", " }, slider), delay);}, slider)(i);\n", " }\n", " }\n", " });\n", " var textInput = $('#textInput'+id+'_'+dim)\n", " textInput.val(init_label);\n", " adjustFontSize(textInput);\n", " });\n", "}\n", "\n", "function init_dropdown(id, plot_id, dim, vals, value, next_vals, labels, next_dim, dim_idx, dynamic) {\n", " var widget = $(\"#_anim_widget\"+id+'_'+dim);\n", " widget.data('values', vals)\n", " for (var i=0; i\", {\n", " value: val,\n", " text: labels[i]\n", " }));\n", " };\n", " widget.data(\"next_vals\", next_vals);\n", " widget.val(value);\n", " widget.on('change', function(event, ui) {\n", " if (dynamic) {\n", " var dim_val = parseInt(this.value);\n", " } else {\n", " var dim_val = $.data(this, 'values')[this.value];\n", " }\n", " var next_vals = $.data(this, \"next_vals\");\n", " if (Object.keys(next_vals).length > 0) {\n", " var new_vals = next_vals[dim_val];\n", " var next_widget = $('#_anim_widget'+id+'_'+next_dim);\n", " update_widget(next_widget, new_vals);\n", " }\n", " var widgets = HoloViews.index[plot_id]\n", " if (widgets) {\n", " widgets.set_frame(dim_val, dim_idx);\n", " }\n", " });\n", "}\n", "\n", "\n", "if (window.HoloViews === undefined) {\n", " window.HoloViews = {}\n", " window.PyViz = window.HoloViews\n", "} else if (window.PyViz === undefined) {\n", " window.PyViz = window.HoloViews\n", "}\n", "\n", "\n", "var _namespace = {\n", " init_slider: init_slider,\n", " init_dropdown: init_dropdown,\n", " comms: {},\n", " comm_status: {},\n", " index: {},\n", " plot_index: {},\n", " kernels: {},\n", " receivers: {}\n", "}\n", "\n", "for (var k in _namespace) {\n", " if (!(k in window.HoloViews)) {\n", " window.HoloViews[k] = _namespace[k];\n", " }\n", "}\n", "\n", "// Define MPL specific subclasses\n", "function MPLSelectionWidget() {\n", " SelectionWidget.apply(this, arguments);\n", "}\n", "\n", "function MPLScrubberWidget() {\n", " ScrubberWidget.apply(this, arguments);\n", "}\n", "\n", "// Let them inherit from the baseclasses\n", "MPLSelectionWidget.prototype = Object.create(SelectionWidget.prototype);\n", "MPLScrubberWidget.prototype = Object.create(ScrubberWidget.prototype);\n", "\n", "// Define methods to override on widgets\n", "var MPLMethods = {\n", " init_slider : function(init_val){\n", " if(this.load_json) {\n", " this.from_json()\n", " } else {\n", " this.update_cache();\n", " }\n", " if (this.dynamic | !this.cached | (this.current_vals === undefined)) {\n", " this.update(0)\n", " } else {\n", " this.set_frame(this.current_vals[0], 0)\n", " }\n", " },\n", " process_msg : function(msg) {\n", " var data = msg.content.data;\n", " this.frames[this.current] = data;\n", " this.update_cache(true);\n", " this.update(this.current);\n", " }\n", "}\n", "// Extend MPL widgets with backend specific methods\n", "extend(MPLSelectionWidget.prototype, MPLMethods);\n", "extend(MPLScrubberWidget.prototype, MPLMethods);\n", "\n", "window.HoloViews.MPLSelectionWidget = MPLSelectionWidget\n", "window.HoloViews.MPLScrubberWidget = MPLScrubberWidget\n", "\n", " function JupyterCommManager() {\n", " }\n", "\n", " JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n", " if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n", " var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n", " comm_manager.register_target(comm_id, function(comm) {\n", " comm.on_msg(msg_handler);\n", " });\n", " } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n", " window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n", " comm.onMsg = msg_handler;\n", " });\n", " }\n", " }\n", "\n", " JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n", " if (comm_id in window.PyViz.comms) {\n", " return window.PyViz.comms[comm_id];\n", " } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n", " var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n", " var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n", " if (msg_handler) {\n", " comm.on_msg(msg_handler);\n", " }\n", " } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n", " var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n", " comm.open();\n", " if (msg_handler) {\n", " comm.onMsg = msg_handler;\n", " }\n", " }\n", "\n", " window.PyViz.comms[comm_id] = comm;\n", " return comm;\n", " }\n", "\n", " window.PyViz.comm_manager = new JupyterCommManager();\n", " \n", "\n", "var JS_MIME_TYPE = 'application/javascript';\n", "var HTML_MIME_TYPE = 'text/html';\n", "var EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\n", "var CLASS_NAME = 'output';\n", "\n", "/**\n", " * Render data to the DOM node\n", " */\n", "function render(props, node) {\n", " var div = document.createElement(\"div\");\n", " var script = document.createElement(\"script\");\n", " node.appendChild(div);\n", " node.appendChild(script);\n", "}\n", "\n", "/**\n", " * Handle when a new output is added\n", " */\n", "function handle_add_output(event, handle) {\n", " var output_area = handle.output_area;\n", " var output = handle.output;\n", " if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n", " return\n", " }\n", " var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", " var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", " if (id !== undefined) {\n", " var nchildren = toinsert.length;\n", " toinsert[nchildren-1].children[0].innerHTML = output.data[HTML_MIME_TYPE];\n", " toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n", " output_area._hv_plot_id = id;\n", " if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n", " window.PyViz.plot_index[id] = Bokeh.index[id];\n", " } else {\n", " window.PyViz.plot_index[id] = null;\n", " }\n", " }\n", "}\n", "\n", "/**\n", " * Handle when an output is cleared or removed\n", " */\n", "function handle_clear_output(event, handle) {\n", " var id = handle.cell.output_area._hv_plot_id;\n", " if ((id === undefined) || !(id in PyViz.plot_index)) { return; }\n", " var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n", " if (comm !== null) {\n", " comm.send({event_type: 'delete', 'id': id});\n", " }\n", " delete PyViz.plot_index[id];\n", " if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n", " var doc = window.Bokeh.index[id].model.document\n", " doc.clear();\n", " const i = window.Bokeh.documents.indexOf(doc);\n", " if (i > -1) {\n", " window.Bokeh.documents.splice(i, 1);\n", " }\n", " }\n", "}\n", "\n", "/**\n", " * Handle kernel restart event\n", " */\n", "function handle_kernel_cleanup(event, handle) {\n", " delete PyViz.comms[\"hv-extension-comm\"];\n", " window.PyViz.plot_index = {}\n", "}\n", "\n", "/**\n", " * Handle update_display_data messages\n", " */\n", "function handle_update_output(event, handle) {\n", " handle_clear_output(event, {cell: {output_area: handle.output_area}})\n", " handle_add_output(event, handle)\n", "}\n", "\n", "function register_renderer(events, OutputArea) {\n", " function append_mime(data, metadata, element) {\n", " // create a DOM node to render to\n", " var toinsert = this.create_output_subarea(\n", " metadata,\n", " CLASS_NAME,\n", " EXEC_MIME_TYPE\n", " );\n", " this.keyboard_manager.register_events(toinsert);\n", " // Render to node\n", " var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", " render(props, toinsert[0]);\n", " element.append(toinsert);\n", " return toinsert\n", " }\n", "\n", " events.on('output_added.OutputArea', handle_add_output);\n", " events.on('output_updated.OutputArea', handle_update_output);\n", " events.on('clear_output.CodeCell', handle_clear_output);\n", " events.on('delete.Cell', handle_clear_output);\n", " events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n", "\n", " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", " safe: true,\n", " index: 0\n", " });\n", "}\n", "\n", "if (window.Jupyter !== undefined) {\n", " try {\n", " var events = require('base/js/events');\n", " var OutputArea = require('notebook/js/outputarea').OutputArea;\n", " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", " register_renderer(events, OutputArea);\n", " }\n", " } catch(err) {\n", " }\n", "}\n" ], "application/vnd.holoviews_load.v0+json": "function HoloViewsWidget() {\n}\n\nHoloViewsWidget.prototype.init_slider = function(init_val){\n if(this.load_json) {\n this.from_json()\n } else {\n this.update_cache();\n }\n}\n\nHoloViewsWidget.prototype.populate_cache = function(idx){\n this.cache[idx].innerHTML = this.frames[idx];\n if (this.embed) {\n delete this.frames[idx];\n }\n}\n\nHoloViewsWidget.prototype.process_error = function(msg){\n}\n\nHoloViewsWidget.prototype.from_json = function() {\n var data_url = this.json_path + this.id + '.json';\n $.getJSON(data_url, $.proxy(function(json_data) {\n this.frames = json_data;\n this.update_cache();\n this.update(0);\n }, this));\n}\n\nHoloViewsWidget.prototype.dynamic_update = function(current){\n if (current === undefined) {\n return\n }\n this.current = current;\n if (this.comm) {\n var msg = {comm_id: this.id+'_client', content: current}\n this.comm.send(msg);\n }\n}\n\nHoloViewsWidget.prototype.update_cache = function(force){\n var frame_len = Object.keys(this.frames).length;\n for (var i=0; i 0) {\n that.time = Date.now();\n that.dynamic_update(that.queue[that.queue.length-1]);\n that.queue = [];\n } else {\n that.wait = false;\n }\n if ((msg.msg_type == \"Ready\") && msg.content) {\n console.log(\"Python callback returned following output:\", msg.content);\n } else if (msg.msg_type == \"Error\") {\n console.log(\"Python failed with the following traceback:\", msg.traceback)\n }\n }\n var comm = HoloViews.comm_manager.get_client_comm(this.plot_id, this.id+'_client', ack_callback);\n return comm\n }\n}\n\nHoloViewsWidget.prototype.msg_handler = function(msg) {\n var metadata = msg.metadata;\n if ((metadata.msg_type == \"Ready\")) {\n if (metadata.content) {\n console.log(\"Python callback returned following output:\", metadata.content);\n }\n\treturn;\n } else if (metadata.msg_type == \"Error\") {\n console.log(\"Python failed with the following traceback:\", metadata.traceback)\n return\n }\n this.process_msg(msg)\n}\n\nHoloViewsWidget.prototype.process_msg = function(msg) {\n}\n\nfunction SelectionWidget(frames, id, slider_ids, keyMap, dim_vals, notFound, load_json, mode, cached, json_path, dynamic, plot_id){\n this.frames = frames;\n this.id = id;\n this.plot_id = plot_id;\n this.slider_ids = slider_ids;\n this.keyMap = keyMap\n this.current_frame = 0;\n this.current_vals = dim_vals;\n this.load_json = load_json;\n this.mode = mode;\n this.notFound = notFound;\n this.cached = cached;\n this.dynamic = dynamic;\n this.cache = {};\n this.json_path = json_path;\n this.init_slider(this.current_vals[0]);\n this.queue = [];\n this.wait = false;\n if (!this.cached || this.dynamic) {\n this.comm = this.init_comms();\n }\n}\n\nSelectionWidget.prototype = new HoloViewsWidget;\n\n\nSelectionWidget.prototype.get_key = function(current_vals) {\n var key = \"(\";\n for (var i=0; i Date.now()))) {\n this.queue.push(key);\n return\n }\n this.queue = [];\n this.time = Date.now();\n this.current_frame = key;\n this.wait = true;\n this.dynamic_update(key)\n } else if (key !== undefined) {\n this.update(key)\n }\n}\n\n\n/* Define the ScrubberWidget class */\nfunction ScrubberWidget(frames, num_frames, id, interval, load_json, mode, cached, json_path, dynamic, plot_id){\n this.slider_id = \"_anim_slider\" + id;\n this.loop_select_id = \"_anim_loop_select\" + id;\n this.id = id;\n this.plot_id = plot_id;\n this.interval = interval;\n this.current_frame = 0;\n this.direction = 0;\n this.dynamic = dynamic;\n this.timer = null;\n this.load_json = load_json;\n this.mode = mode;\n this.cached = cached;\n this.frames = frames;\n this.cache = {};\n this.length = num_frames;\n this.json_path = json_path;\n document.getElementById(this.slider_id).max = this.length - 1;\n this.init_slider(0);\n this.wait = false;\n this.queue = [];\n if (!this.cached || this.dynamic) {\n this.comm = this.init_comms()\n }\n}\n\nScrubberWidget.prototype = new HoloViewsWidget;\n\nScrubberWidget.prototype.set_frame = function(frame){\n this.current_frame = frame;\n var widget = document.getElementById(this.slider_id);\n if (widget === null) {\n this.pause_animation();\n return\n }\n widget.value = this.current_frame;\n if (this.dynamic || !this.cached) {\n if ((this.time !== undefined) && ((this.wait) && ((this.time + 10000) > Date.now()))) {\n this.queue.push(frame);\n return\n }\n this.queue = [];\n this.time = Date.now();\n this.wait = true;\n this.dynamic_update(frame)\n } else {\n this.update(frame)\n }\n}\n\nScrubberWidget.prototype.get_loop_state = function(){\n var button_group = document[this.loop_select_id].state;\n for (var i = 0; i < button_group.length; i++) {\n var button = button_group[i];\n if (button.checked) {\n return button.value;\n }\n }\n return undefined;\n}\n\n\nScrubberWidget.prototype.next_frame = function() {\n this.set_frame(Math.min(this.length - 1, this.current_frame + 1));\n}\n\nScrubberWidget.prototype.previous_frame = function() {\n this.set_frame(Math.max(0, this.current_frame - 1));\n}\n\nScrubberWidget.prototype.first_frame = function() {\n this.set_frame(0);\n}\n\nScrubberWidget.prototype.last_frame = function() {\n this.set_frame(this.length - 1);\n}\n\nScrubberWidget.prototype.slower = function() {\n this.interval /= 0.7;\n if(this.direction > 0){this.play_animation();}\n else if(this.direction < 0){this.reverse_animation();}\n}\n\nScrubberWidget.prototype.faster = function() {\n this.interval *= 0.7;\n if(this.direction > 0){this.play_animation();}\n else if(this.direction < 0){this.reverse_animation();}\n}\n\nScrubberWidget.prototype.anim_step_forward = function() {\n if(this.current_frame < this.length - 1){\n this.next_frame();\n }else{\n var loop_state = this.get_loop_state();\n if(loop_state == \"loop\"){\n this.first_frame();\n }else if(loop_state == \"reflect\"){\n this.last_frame();\n this.reverse_animation();\n }else{\n this.pause_animation();\n this.last_frame();\n }\n }\n}\n\nScrubberWidget.prototype.anim_step_reverse = function() {\n if(this.current_frame > 0){\n this.previous_frame();\n } else {\n var loop_state = this.get_loop_state();\n if(loop_state == \"loop\"){\n this.last_frame();\n }else if(loop_state == \"reflect\"){\n this.first_frame();\n this.play_animation();\n }else{\n this.pause_animation();\n this.first_frame();\n }\n }\n}\n\nScrubberWidget.prototype.pause_animation = function() {\n this.direction = 0;\n if (this.timer){\n clearInterval(this.timer);\n this.timer = null;\n }\n}\n\nScrubberWidget.prototype.play_animation = function() {\n this.pause_animation();\n this.direction = 1;\n var t = this;\n if (!this.timer) this.timer = setInterval(function(){t.anim_step_forward();}, this.interval);\n}\n\nScrubberWidget.prototype.reverse_animation = function() {\n this.pause_animation();\n this.direction = -1;\n var t = this;\n if (!this.timer) this.timer = setInterval(function(){t.anim_step_reverse();}, this.interval);\n}\n\nfunction extend(destination, source) {\n for (var k in source) {\n if (source.hasOwnProperty(k)) {\n destination[k] = source[k];\n }\n }\n return destination;\n}\n\nfunction update_widget(widget, values) {\n if (widget.hasClass(\"ui-slider\")) {\n widget.slider('option', {\n min: 0,\n max: values.length-1,\n dim_vals: values,\n value: 0,\n dim_labels: values\n })\n widget.slider('option', 'slide').call(widget, event, {value: 0})\n } else {\n widget.empty();\n for (var i=0; i\", {\n value: i,\n text: values[i]\n }))\n };\n widget.data('values', values);\n widget.data('value', 0);\n widget.trigger(\"change\");\n };\n}\n\nfunction init_slider(id, plot_id, dim, values, next_vals, labels, dynamic, step, value, next_dim,\n dim_idx, delay, jQueryUI_CDN, UNDERSCORE_CDN) {\n // Slider JS Block START\n function loadcssfile(filename){\n var fileref=document.createElement(\"link\")\n fileref.setAttribute(\"rel\", \"stylesheet\")\n fileref.setAttribute(\"type\", \"text/css\")\n fileref.setAttribute(\"href\", filename)\n document.getElementsByTagName(\"head\")[0].appendChild(fileref)\n }\n loadcssfile(\"https://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css\");\n /* Check if jQuery and jQueryUI have been loaded\n otherwise load with require.js */\n var jQuery = window.jQuery,\n // check for old versions of jQuery\n oldjQuery = jQuery && !!jQuery.fn.jquery.match(/^1\\.[0-4](\\.|$)/),\n jquery_path = '',\n paths = {},\n noConflict;\n var jQueryUI = jQuery.ui;\n // check for jQuery\n if (!jQuery || oldjQuery) {\n // load if it's not available or doesn't meet min standards\n paths.jQuery = jQuery;\n noConflict = !!oldjQuery;\n } else {\n // register the current jQuery\n define('jquery', [], function() { return jQuery; });\n }\n if (!jQueryUI) {\n paths.jQueryUI = jQueryUI_CDN.slice(null, -3);\n } else {\n define('jQueryUI', [], function() { return jQuery.ui; });\n }\n paths.underscore = UNDERSCORE_CDN.slice(null, -3);\n var jquery_require = {\n paths: paths,\n shim: {\n \"jQueryUI\": {\n exports:\"$\",\n deps: ['jquery']\n },\n \"underscore\": {\n exports: '_'\n }\n }\n }\n require.config(jquery_require);\n require([\"jQueryUI\", \"underscore\"], function(jUI, _){\n if (noConflict) $.noConflict(true);\n var vals = values;\n if (dynamic && vals.constructor === Array) {\n var default_value = parseFloat(value);\n var min = parseFloat(vals[0]);\n var max = parseFloat(vals[vals.length-1]);\n var wstep = step;\n var wlabels = [default_value];\n var init_label = default_value;\n } else {\n var min = 0;\n if (dynamic) {\n var max = Object.keys(vals).length - 1;\n var init_label = labels[value];\n var default_value = values[value];\n } else {\n var max = vals.length - 1;\n var init_label = labels[value];\n var default_value = value;\n }\n var wstep = 1;\n var wlabels = labels;\n }\n function adjustFontSize(text) {\n var width_ratio = (text.parent().width()/8)/text.val().length;\n var size = Math.min(0.9, Math.max(0.6, width_ratio))+'em';\n text.css('font-size', size);\n }\n var slider = $('#_anim_widget'+id+'_'+dim);\n slider.slider({\n animate: \"fast\",\n min: min,\n max: max,\n step: wstep,\n value: default_value,\n dim_vals: vals,\n dim_labels: wlabels,\n next_vals: next_vals,\n slide: function(event, ui) {\n var vals = slider.slider(\"option\", \"dim_vals\");\n var next_vals = slider.slider(\"option\", \"next_vals\");\n var dlabels = slider.slider(\"option\", \"dim_labels\");\n if (dynamic) {\n var dim_val = ui.value;\n if (vals.constructor === Array) {\n var label = ui.value;\n } else {\n var label = dlabels[ui.value];\n }\n } else {\n var dim_val = vals[ui.value];\n var label = dlabels[ui.value];\n }\n var text = $('#textInput'+id+'_'+dim);\n text.val(label);\n adjustFontSize(text);\n HoloViews.index[plot_id].set_frame(dim_val, dim_idx);\n if (Object.keys(next_vals).length > 0) {\n var new_vals = next_vals[dim_val];\n var next_widget = $('#_anim_widget'+id+'_'+next_dim);\n update_widget(next_widget, new_vals);\n }\n }\n });\n slider.keypress(function(event) {\n if (event.which == 80 || event.which == 112) {\n var start = slider.slider(\"option\", \"value\");\n var stop = slider.slider(\"option\", \"max\");\n for (var i=start; i<=stop; i++) {\n var delay = i*delay;\n $.proxy(function doSetTimeout(i) { setTimeout($.proxy(function() {\n var val = {value:i};\n slider.slider('value',i);\n slider.slider(\"option\", \"slide\")(null, val);\n }, slider), delay);}, slider)(i);\n }\n }\n if (event.which == 82 || event.which == 114) {\n var start = slider.slider(\"option\", \"value\");\n var stop = slider.slider(\"option\", \"min\");\n var count = 0;\n for (var i=start; i>=stop; i--) {\n var delay = count*delay;\n count = count + 1;\n $.proxy(function doSetTimeout(i) { setTimeout($.proxy(function() {\n var val = {value:i};\n slider.slider('value',i);\n slider.slider(\"option\", \"slide\")(null, val);\n }, slider), delay);}, slider)(i);\n }\n }\n });\n var textInput = $('#textInput'+id+'_'+dim)\n textInput.val(init_label);\n adjustFontSize(textInput);\n });\n}\n\nfunction init_dropdown(id, plot_id, dim, vals, value, next_vals, labels, next_dim, dim_idx, dynamic) {\n var widget = $(\"#_anim_widget\"+id+'_'+dim);\n widget.data('values', vals)\n for (var i=0; i\", {\n value: val,\n text: labels[i]\n }));\n };\n widget.data(\"next_vals\", next_vals);\n widget.val(value);\n widget.on('change', function(event, ui) {\n if (dynamic) {\n var dim_val = parseInt(this.value);\n } else {\n var dim_val = $.data(this, 'values')[this.value];\n }\n var next_vals = $.data(this, \"next_vals\");\n if (Object.keys(next_vals).length > 0) {\n var new_vals = next_vals[dim_val];\n var next_widget = $('#_anim_widget'+id+'_'+next_dim);\n update_widget(next_widget, new_vals);\n }\n var widgets = HoloViews.index[plot_id]\n if (widgets) {\n widgets.set_frame(dim_val, dim_idx);\n }\n });\n}\n\n\nif (window.HoloViews === undefined) {\n window.HoloViews = {}\n window.PyViz = window.HoloViews\n} else if (window.PyViz === undefined) {\n window.PyViz = window.HoloViews\n}\n\n\nvar _namespace = {\n init_slider: init_slider,\n init_dropdown: init_dropdown,\n comms: {},\n comm_status: {},\n index: {},\n plot_index: {},\n kernels: {},\n receivers: {}\n}\n\nfor (var k in _namespace) {\n if (!(k in window.HoloViews)) {\n window.HoloViews[k] = _namespace[k];\n }\n}\n\n// Define MPL specific subclasses\nfunction MPLSelectionWidget() {\n SelectionWidget.apply(this, arguments);\n}\n\nfunction MPLScrubberWidget() {\n ScrubberWidget.apply(this, arguments);\n}\n\n// Let them inherit from the baseclasses\nMPLSelectionWidget.prototype = Object.create(SelectionWidget.prototype);\nMPLScrubberWidget.prototype = Object.create(ScrubberWidget.prototype);\n\n// Define methods to override on widgets\nvar MPLMethods = {\n init_slider : function(init_val){\n if(this.load_json) {\n this.from_json()\n } else {\n this.update_cache();\n }\n if (this.dynamic | !this.cached | (this.current_vals === undefined)) {\n this.update(0)\n } else {\n this.set_frame(this.current_vals[0], 0)\n }\n },\n process_msg : function(msg) {\n var data = msg.content.data;\n this.frames[this.current] = data;\n this.update_cache(true);\n this.update(this.current);\n }\n}\n// Extend MPL widgets with backend specific methods\nextend(MPLSelectionWidget.prototype, MPLMethods);\nextend(MPLScrubberWidget.prototype, MPLMethods);\n\nwindow.HoloViews.MPLSelectionWidget = MPLSelectionWidget\nwindow.HoloViews.MPLScrubberWidget = MPLScrubberWidget\n\n function JupyterCommManager() {\n }\n\n JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n comm_manager.register_target(comm_id, function(comm) {\n comm.on_msg(msg_handler);\n });\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n comm.onMsg = msg_handler;\n });\n }\n }\n\n JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n if (comm_id in window.PyViz.comms) {\n return window.PyViz.comms[comm_id];\n } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n if (msg_handler) {\n comm.on_msg(msg_handler);\n }\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n comm.open();\n if (msg_handler) {\n comm.onMsg = msg_handler;\n }\n }\n\n window.PyViz.comms[comm_id] = comm;\n return comm;\n }\n\n window.PyViz.comm_manager = new JupyterCommManager();\n \n\nvar JS_MIME_TYPE = 'application/javascript';\nvar HTML_MIME_TYPE = 'text/html';\nvar EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\nvar CLASS_NAME = 'output';\n\n/**\n * Render data to the DOM node\n */\nfunction render(props, node) {\n var div = document.createElement(\"div\");\n var script = document.createElement(\"script\");\n node.appendChild(div);\n node.appendChild(script);\n}\n\n/**\n * Handle when a new output is added\n */\nfunction handle_add_output(event, handle) {\n var output_area = handle.output_area;\n var output = handle.output;\n if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n return\n }\n var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n if (id !== undefined) {\n var nchildren = toinsert.length;\n toinsert[nchildren-1].children[0].innerHTML = output.data[HTML_MIME_TYPE];\n toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n output_area._hv_plot_id = id;\n if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n window.PyViz.plot_index[id] = Bokeh.index[id];\n } else {\n window.PyViz.plot_index[id] = null;\n }\n }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n var id = handle.cell.output_area._hv_plot_id;\n if ((id === undefined) || !(id in PyViz.plot_index)) { return; }\n var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n if (comm !== null) {\n comm.send({event_type: 'delete', 'id': id});\n }\n delete PyViz.plot_index[id];\n if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n var doc = window.Bokeh.index[id].model.document\n doc.clear();\n const i = window.Bokeh.documents.indexOf(doc);\n if (i > -1) {\n window.Bokeh.documents.splice(i, 1);\n }\n }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n delete PyViz.comms[\"hv-extension-comm\"];\n window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n handle_clear_output(event, {cell: {output_area: handle.output_area}})\n handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n var toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[0]);\n element.append(toinsert);\n return toinsert\n }\n\n events.on('output_added.OutputArea', handle_add_output);\n events.on('output_updated.OutputArea', handle_update_output);\n events.on('clear_output.CodeCell', handle_clear_output);\n events.on('delete.Cell', handle_clear_output);\n events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n safe: true,\n index: 0\n });\n}\n\nif (window.Jupyter !== undefined) {\n try {\n var events = require('base/js/events');\n var OutputArea = require('notebook/js/outputarea').OutputArea;\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n } catch(err) {\n }\n}\n" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import numpy as np\n", "import holoviews as hv\n", "hv.extension('matplotlib', logo=False)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "x = np.linspace(-15, 15, num=100)\n", "y = np.linspace(-15, 15, num=100)\n", "\n", "X, Y = np.meshgrid(x, y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using this grid, we can build a phase map for a monopole source. By phase map, I mean the change in phase, at the selected frequency that a radial wave would incur by propagating from the origin to the point of the grid." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see HoloViews #2574 for more information\n", "if (!(document.getElementById('1259344920304')) && !(document.getElementById('_anim_imgNone'))) {\n", " console.log(\"Creating DOM nodes dynamically for assumed nbconvert export. To generate clean HTML output set HV_DOC_HTML as an environment variable.\")\n", " var htmlObject = document.createElement('div');\n", " htmlObject.innerHTML = `
`;\n", " var scriptTags = document.getElementsByTagName('script');\n", " var parentTag = scriptTags[scriptTags.length-1].parentNode;\n", " if (parentTag.attributes.length && (parentTag.attributes[0].name == 'data-shell-mode')) {\n", " alert('Displaying PyViz objects in JupyterLab requires the jupyterlab_pyviz extension to be installed, install it with:\\n\\n\\tjupyter labextension install @pyviz/jupyterlab_pyviz');\n", " } else {\n", " parentTag.append(htmlObject)\n", " }\n", "}\n" ], "application/vnd.holoviews_exec.v0+json": "", "text/html": [ "
" ], "text/plain": [ ":QuadMesh [x,y] (z)" ] }, "execution_count": 3, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 1259344920304 } }, "output_type": "execute_result" } ], "source": [ "r = np.sqrt(X**2 + Y**2)\n", "phase_map1= np.exp(1j * r)\n", "\n", "hv.QuadMesh((X, Y, np.real(phase_map1)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can animate this phase map by adding a phase term that varies as a function of time. If we discretize a single period in a given number of frames and loop over the frames, we can generate an \"infinite\" animation. Let's try this!" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "N = 15" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see HoloViews #2574 for more information\n", "if (!(document.getElementById('1259389594760')) && !(document.getElementById('_anim_img9ed3733050a044978d1c58964f5fe1f5'))) {\n", " console.log(\"Creating DOM nodes dynamically for assumed nbconvert export. To generate clean HTML output set HV_DOC_HTML as an environment variable.\")\n", " var htmlObject = document.createElement('div');\n", " htmlObject.innerHTML = `
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Once \n", " Loop \n", " Reflect \n", "
\n", "
`;\n", " var scriptTags = document.getElementsByTagName('script');\n", " var parentTag = scriptTags[scriptTags.length-1].parentNode;\n", " if (parentTag.attributes.length && (parentTag.attributes[0].name == 'data-shell-mode')) {\n", " alert('Displaying PyViz objects in JupyterLab requires the jupyterlab_pyviz extension to be installed, install it with:\\n\\n\\tjupyter labextension install @pyviz/jupyterlab_pyviz');\n", " } else {\n", " parentTag.append(htmlObject)\n", " }\n", "}\n", "/* Instantiate the MPLScrubberWidget class. */\n", "/* The IDs given should match those used in the template above. */\n", "function create_widget() {\n", " var frame_data = {\"0\": \"\", \"1\": \"\", \"2\": \"\", \"3\": \"\", \"4\": \"\", \"5\": \"\", \"6\": \"\", \"7\": \"\", \"8\": \"\", \"9\": \"\", \"10\": \"\", \"11\": \"\", \"12\": \"\", \"13\": \"\", \"14\": \"\"};\n", " var anim = new HoloViews.MPLScrubberWidget(frame_data, 15, \"9ed3733050a044978d1c58964f5fe1f5\", 50, false, \"default\", true, \"./json_figures/\", false, \"1259389594760\");\n", " HoloViews.index['1259389594760'] = anim;\n", "}\n", "\n", "create_widget();\n", "\n" ], "application/vnd.holoviews_exec.v0+json": "", "text/html": [ "
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Once \n", " Loop \n", " Reflect \n", "
\n", "
" ], "text/plain": [ ":HoloMap [Default]\n", " :QuadMesh [x,y] (z)" ] }, "execution_count": 5, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 1259389594760 } }, "output_type": "execute_result" } ], "source": [ "%%output holomap='scrubber'\n", "hmap1 = hv.HoloMap({i: hv.QuadMesh((X, Y, np.real(phase_map1* np.exp(-1j * i / N * 2 * np.pi))), \n", " label='monopole') for i in range(N)}).opts(colorbar=True)\n", "hmap1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's apply the same logic to a dipole, made out of two sources a fraction of a wavelength apart and out of phase." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "delta = 2.5\n", "r1 = np.sqrt((X-delta)**2 + Y**2)\n", "r2 = np.sqrt((X+delta)**2 + Y**2)\n", "phase_map2 = .5 * np.exp(1j * r1) - .5 * np.exp(1j * r2)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see HoloViews #2574 for more information\n", "if (!(document.getElementById('1259390935616')) && !(document.getElementById('_anim_img218639b7507e43a98a6475be1af67864'))) {\n", " console.log(\"Creating DOM nodes dynamically for assumed nbconvert export. To generate clean HTML output set HV_DOC_HTML as an environment variable.\")\n", " var htmlObject = document.createElement('div');\n", " htmlObject.innerHTML = `
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Once \n", " Loop \n", " Reflect \n", "
\n", "
`;\n", " var scriptTags = document.getElementsByTagName('script');\n", " var parentTag = scriptTags[scriptTags.length-1].parentNode;\n", " if (parentTag.attributes.length && (parentTag.attributes[0].name == 'data-shell-mode')) {\n", " alert('Displaying PyViz objects in JupyterLab requires the jupyterlab_pyviz extension to be installed, install it with:\\n\\n\\tjupyter labextension install @pyviz/jupyterlab_pyviz');\n", " } else {\n", " parentTag.append(htmlObject)\n", " }\n", "}\n", "/* Instantiate the MPLScrubberWidget class. */\n", "/* The IDs given should match those used in the template above. */\n", "function create_widget() {\n", " var frame_data = {\"0\": \"\", \"1\": \"\", \"2\": \"\", \"3\": \"\", \"4\": \"\", \"5\": \"\", \"6\": \"\", \"7\": \"\", \"8\": \"\", \"9\": \"\", \"10\": \"\", \"11\": \"\", \"12\": \"\", \"13\": \"\", \"14\": \"\"};\n", " var anim = new HoloViews.MPLScrubberWidget(frame_data, 15, \"218639b7507e43a98a6475be1af67864\", 50, false, \"default\", true, \"./json_figures/\", false, \"1259390935616\");\n", " HoloViews.index['1259390935616'] = anim;\n", "}\n", "\n", "create_widget();\n", "\n" ], "application/vnd.holoviews_exec.v0+json": "", "text/html": [ "
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Once \n", " Loop \n", " Reflect \n", "
\n", "
" ], "text/plain": [ ":HoloMap [Default]\n", " :QuadMesh [x,y] (z)" ] }, "execution_count": 7, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 1259390935616 } }, "output_type": "execute_result" } ], "source": [ "%%output holomap='scrubber' \n", "hmap2 = hv.HoloMap({i: hv.QuadMesh((X, Y, np.real(phase_map2 * np.exp(-1j * i / N * 2 * np.pi))), \n", " label='dipole') for i in range(N)}).opts(colorbar=True)\n", "hmap2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, the same formulas apply to a quadripole source, which is made out of four sources, two of them being out of phase." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "gamma = 4.5\n", "r1 = np.sqrt((X - gamma)**2 + (Y - gamma)**2)\n", "r2 = np.sqrt((X - gamma)**2 + (Y + gamma)**2)\n", "r3 = np.sqrt((X + gamma)**2 + (Y - gamma)**2)\n", "r4 = np.sqrt((X + gamma)**2 + (Y + gamma)**2)\n", "phase_map3 = .25 * np.exp(1j * r1) - .25 * np.exp(1j * r2) + .25 * np.exp(1j * r3) - .25 * np.exp(1j * r4)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see HoloViews #2574 for more information\n", "if (!(document.getElementById('1259408563504')) && !(document.getElementById('_anim_imgd634151f6ca14020af92fc80e584ab8f'))) {\n", " console.log(\"Creating DOM nodes dynamically for assumed nbconvert export. To generate clean HTML output set HV_DOC_HTML as an environment variable.\")\n", " var htmlObject = document.createElement('div');\n", " htmlObject.innerHTML = `
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Once \n", " Loop \n", " Reflect \n", "
\n", "
`;\n", " var scriptTags = document.getElementsByTagName('script');\n", " var parentTag = scriptTags[scriptTags.length-1].parentNode;\n", " if (parentTag.attributes.length && (parentTag.attributes[0].name == 'data-shell-mode')) {\n", " alert('Displaying PyViz objects in JupyterLab requires the jupyterlab_pyviz extension to be installed, install it with:\\n\\n\\tjupyter labextension install @pyviz/jupyterlab_pyviz');\n", " } else {\n", " parentTag.append(htmlObject)\n", " }\n", "}\n", "/* Instantiate the MPLScrubberWidget class. */\n", "/* The IDs given should match those used in the template above. */\n", "function create_widget() {\n", " var frame_data = {\"0\": \"\", \"1\": \"\", \"2\": \"\", \"3\": \"\", \"4\": \"\", \"5\": \"\", \"6\": \"\", \"7\": \"\", \"8\": \"\", \"9\": \"\", \"10\": \"\", \"11\": \"\", \"12\": \"\", \"13\": \"\", \"14\": \"\"};\n", " var anim = new HoloViews.MPLScrubberWidget(frame_data, 15, \"d634151f6ca14020af92fc80e584ab8f\", 50, false, \"default\", true, \"./json_figures/\", false, \"1259408563504\");\n", " HoloViews.index['1259408563504'] = anim;\n", "}\n", "\n", "create_widget();\n", "\n" ], "application/vnd.holoviews_exec.v0+json": "", "text/html": [ "
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Once \n", " Loop \n", " Reflect \n", "
\n", "
" ], "text/plain": [ ":HoloMap [Default]\n", " :QuadMesh [x,y] (z)" ] }, "execution_count": 9, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 1259408563504 } }, "output_type": "execute_result" } ], "source": [ "%%output holomap='scrubber'\n", "hmap3 = hv.HoloMap({i: hv.QuadMesh((X, Y, np.real(phase_map3 * np.exp(-1j * i / N * 2 * np.pi))), \n", " label='quadrupole') for i in range(N)}).opts(colorbar=True)\n", "hmap3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As a final step, let's add a point cloud of particle moving on top of these monopole source. We first randomly create some points:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "M = 400\n", "Xm = x.min() + np.random.rand(M) * (x.max() - x.min())\n", "Ym = y.min() + np.random.rand(M) * (y.max() - y.min())\n", "Rm = np.sqrt(Xm**2 + Ym**2)\n", "Tm = np.arctan2(Ym, Xm)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Build a phase map, using the same formula as before, but inserting the coordinates of the points." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "points = np.c_[Rm * np.cos(Tm), Rm * np.sin(Tm)]\n", "phase_map4 = 1j * np.exp(1j * 1 * Rm)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And then we animate it using an oscillation in time." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see HoloViews #2574 for more information\n", "if (!(document.getElementById('1259409456432')) && !(document.getElementById('_anim_img2c03c59f8ad54ed6bfbf18e1358426f7'))) {\n", " console.log(\"Creating DOM nodes dynamically for assumed nbconvert export. To generate clean HTML output set HV_DOC_HTML as an environment variable.\")\n", " var htmlObject = document.createElement('div');\n", " htmlObject.innerHTML = `
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Once \n", " Loop \n", " Reflect \n", "
\n", "
`;\n", " var scriptTags = document.getElementsByTagName('script');\n", " var parentTag = scriptTags[scriptTags.length-1].parentNode;\n", " if (parentTag.attributes.length && (parentTag.attributes[0].name == 'data-shell-mode')) {\n", " alert('Displaying PyViz objects in JupyterLab requires the jupyterlab_pyviz extension to be installed, install it with:\\n\\n\\tjupyter labextension install @pyviz/jupyterlab_pyviz');\n", " } else {\n", " parentTag.append(htmlObject)\n", " }\n", "}\n", "/* Instantiate the MPLScrubberWidget class. */\n", "/* The IDs given should match those used in the template above. */\n", "function create_widget() {\n", " var frame_data = {\"0\": \"\", \"1\": \"\", \"2\": \"\", \"3\": \"\", \"4\": \"\", \"5\": \"\", \"6\": \"\", \"7\": \"\", \"8\": \"\", \"9\": \"\", \"10\": \"\", \"11\": \"\", \"12\": \"\", \"13\": \"\", \"14\": \"\"};\n", " var anim = new HoloViews.MPLScrubberWidget(frame_data, 15, \"2c03c59f8ad54ed6bfbf18e1358426f7\", 50, false, \"default\", true, \"./json_figures/\", false, \"1259409456432\");\n", " HoloViews.index['1259409456432'] = anim;\n", "}\n", "\n", "create_widget();\n", "\n" ], "application/vnd.holoviews_exec.v0+json": "", "text/html": [ "
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Once \n", " Loop \n", " Reflect \n", "
\n", "
" ], "text/plain": [ ":HoloMap [Default]\n", " :Points [x,y]" ] }, "execution_count": 12, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 1259409456432 } }, "output_type": "execute_result" } ], "source": [ "%%output holomap='scrubber'\n", "hmap4 = hv.HoloMap({i: hv.Points(points + np.c_[np.real(phase_map4 * np.cos(Tm) * np.exp(-1j * i / N * 2 * np.pi)),\n", " np.real(phase_map4 * np.sin(Tm) * np.exp(-1j * i / N * 2 * np.pi))], \n", " label='points') for i in range(N)})\n", "hmap4" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The structure of the wave is visualized thanks to the particle displacement." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using the nice plotting API of `holoviews`, we can even overlay this animation with our previous monopole field animation:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "\n", "// Ugly hack - see HoloViews #2574 for more information\n", "if (!(document.getElementById('1259391592936')) && !(document.getElementById('_anim_imgdf0e40259d564fa0898cdd5d2a7beb03'))) {\n", " console.log(\"Creating DOM nodes dynamically for assumed nbconvert export. To generate clean HTML output set HV_DOC_HTML as an environment variable.\")\n", " var htmlObject = document.createElement('div');\n", " htmlObject.innerHTML = `
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Once \n", " Loop \n", " Reflect \n", "
\n", "
`;\n", " var scriptTags = document.getElementsByTagName('script');\n", " var parentTag = scriptTags[scriptTags.length-1].parentNode;\n", " if (parentTag.attributes.length && (parentTag.attributes[0].name == 'data-shell-mode')) {\n", " alert('Displaying PyViz objects in JupyterLab requires the jupyterlab_pyviz extension to be installed, install it with:\\n\\n\\tjupyter labextension install @pyviz/jupyterlab_pyviz');\n", " } else {\n", " parentTag.append(htmlObject)\n", " }\n", "}\n", "/* Instantiate the MPLScrubberWidget class. */\n", "/* The IDs given should match those used in the template above. */\n", "function create_widget() {\n", " var frame_data = {\"0\": \"\", \"1\": \"\", \"2\": \"\", \"3\": \"\", \"4\": \"\", \"5\": \"\", \"6\": \"\", \"7\": \"\", \"8\": \"\", \"9\": \"\", \"10\": \"\", \"11\": \"\", \"12\": \"\", \"13\": \"\", \"14\": \"\"};\n", " var anim = new HoloViews.MPLScrubberWidget(frame_data, 15, \"df0e40259d564fa0898cdd5d2a7beb03\", 50, false, \"default\", true, \"./json_figures/\", false, \"1259391592936\");\n", " HoloViews.index['1259391592936'] = anim;\n", "}\n", "\n", "create_widget();\n", "\n" ], "application/vnd.holoviews_exec.v0+json": "", "text/html": [ "
\n", "
\n", " \n", " \n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Once \n", " Loop \n", " Reflect \n", "
\n", "
" ], "text/plain": [ ":HoloMap [Default]\n", " :Overlay\n", " .QuadMesh.Monopole :QuadMesh [x,y] (z)\n", " .Points.Points :Points [x,y]" ] }, "execution_count": 13, "metadata": { "application/vnd.holoviews_exec.v0+json": { "id": 1259391592936 } }, "output_type": "execute_result" } ], "source": [ "%%output holomap='scrubber'\n", "hmap1 * hmap4.opts(show_legend=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you like this sort of animations, a fantastic resource for endless variations on the \"wavy\" theme can be found at [Bees and bombs](https://twitter.com/beesandbombs)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*This post was entirely written using the IPython notebook. Its content is BSD-licensed. You can see a static view or download this notebook with the help of nbviewer at [20190129_MonopoleDipoleQuadrupole.ipynb](http://nbviewer.ipython.org/urls/raw.github.com/flothesof/posts/master/20190129_MonopoleDipoleQuadrupole.ipynb).*" ] } ], "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.7.1" } }, "nbformat": 4, "nbformat_minor": 2 }