Skip to content

Instantly share code, notes, and snippets.

@mirrornerror
Last active April 30, 2021 10:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mirrornerror/b21087835d726439bbdf08b42e73470e to your computer and use it in GitHub Desktop.
Save mirrornerror/b21087835d726439bbdf08b42e73470e to your computer and use it in GitHub Desktop.
IK_FABRIK_AngleLimit
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"metadata": {},
"cell_type": "markdown",
"source": "## Inverse Kinematics: FABRIK(Foward and Backward Reaching Kinematics)"
},
{
"metadata": {
"ExecuteTime": {
"start_time": "2021-03-28T06:13:55.097449Z",
"end_time": "2021-03-28T06:13:55.604976Z"
},
"execution": {
"iopub.execute_input": "2021-03-09T19:28:06.847Z",
"iopub.status.busy": "2021-03-09T19:28:06.829Z",
"iopub.status.idle": "2021-03-09T19:28:06.867Z",
"shell.execute_reply": "2021-03-09T19:28:07.034Z"
},
"trusted": true
},
"cell_type": "code",
"source": "%matplotlib notebook\nimport numpy as np\nimport matplotlib.pyplot as plt",
"execution_count": 1,
"outputs": []
},
{
"metadata": {
"ExecuteTime": {
"start_time": "2021-03-28T06:13:55.607680Z",
"end_time": "2021-03-28T06:13:55.656280Z"
},
"execution": {
"iopub.execute_input": "2021-03-09T19:28:06.922Z",
"iopub.status.busy": "2021-03-09T19:28:06.898Z",
"iopub.status.idle": "2021-03-09T19:28:06.936Z",
"shell.execute_reply": "2021-03-09T19:28:07.040Z"
},
"trusted": true
},
"cell_type": "code",
"source": "class Arm:\n def __init__(self, _ax, _ay, _length, _angle):\n self.ax = _ax\n self.ay = _ay\n self.length = _length\n self.angle = _angle\n self.bx = self.ax + self.length * np.cos(self.angle)\n self.by = self.ay + self.length * np.sin(self.angle)\n\n def backward(self, tx, ty):\n theta = np.arctan2(ty - self.ay, tx - self.ax)\n self.ax = tx - self.length * np.cos(theta)\n self.ay = ty - self.length * np.sin(theta)\n self.bx = tx\n self.by = ty\n \n def forward(self, baseX, baseY):\n theta = np.arctan2(self.by - baseY, self.bx - baseX)\n self.ax = baseX\n self.ay = baseY\n self.bx = baseX + self.length * np.cos(theta)\n self.by = baseY + self.length * np.sin(theta)",
"execution_count": 2,
"outputs": []
},
{
"metadata": {
"ExecuteTime": {
"start_time": "2021-03-28T06:13:55.659873Z",
"end_time": "2021-03-28T06:13:55.763283Z"
},
"trusted": true
},
"cell_type": "code",
"source": "N = 4 # the number of link\nOX = 0 # offset x: Base coodinates\nOY = 0 # offset y: Base coodinates\n\nfab = []\nfor i in range(N):\n linkLength = 1 - i * 0.1\n initTheta = np.pi / 2 # vertical position\n if i == 0:\n fab.append(Arm(OX, OY, linkLength, initTheta))\n else:\n fab.append(Arm(fab[i-1].bx, fab[i-1].by, linkLength, initTheta))\n \nfabX = []\nfabY = []\nfor i in range(N):\n fabX.append([fab[i].ax, fab[i].bx])\n fabY.append([fab[i].ay, fab[i].by])\n \nfig = plt.figure(figsize=(5,5))\nax = fig.add_subplot(111)\nax.axis([-1,N,-1,N])\nax.grid()\nax.plot(fabX, fabY, color='orange')\nax.scatter(fabX, fabY, color='orange')",
"execution_count": 3,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/plain": "<IPython.core.display.Javascript object>",
"application/javascript": "/* Put everything inside the global mpl namespace */\n/* global mpl */\nwindow.mpl = {};\n\nmpl.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(\n '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\nmpl.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 = document.createElement('div');\n this.root.setAttribute('style', 'display: inline-block');\n this._root_extra_style(this.root);\n\n parent_element.appendChild(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 (fig.ratio !== 1) {\n fig.send_message('set_dpi_ratio', { dpi_ratio: fig.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\nmpl.figure.prototype._init_header = function () {\n var titlebar = document.createElement('div');\n titlebar.classList =\n 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n var titletext = document.createElement('div');\n titletext.classList = 'ui-dialog-title';\n titletext.setAttribute(\n 'style',\n 'width: 100%; text-align: center; padding: 3px;'\n );\n titlebar.appendChild(titletext);\n this.root.appendChild(titlebar);\n this.header = titletext;\n};\n\nmpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._init_canvas = function () {\n var fig = this;\n\n var canvas_div = (this.canvas_div = document.createElement('div'));\n canvas_div.setAttribute(\n 'style',\n 'border: 1px solid #ddd;' +\n 'box-sizing: content-box;' +\n 'clear: both;' +\n 'min-height: 1px;' +\n 'min-width: 1px;' +\n 'outline: 0;' +\n 'overflow: hidden;' +\n 'position: relative;' +\n 'resize: both;'\n );\n\n function on_keyboard_event_closure(name) {\n return function (event) {\n return fig.key_event(event, name);\n };\n }\n\n canvas_div.addEventListener(\n 'keydown',\n on_keyboard_event_closure('key_press')\n );\n canvas_div.addEventListener(\n 'keyup',\n on_keyboard_event_closure('key_release')\n );\n\n this._canvas_extra_style(canvas_div);\n this.root.appendChild(canvas_div);\n\n var canvas = (this.canvas = document.createElement('canvas'));\n canvas.classList.add('mpl-canvas');\n canvas.setAttribute('style', 'box-sizing: content-box;');\n\n this.context = canvas.getContext('2d');\n\n var backingStore =\n this.context.backingStorePixelRatio ||\n this.context.webkitBackingStorePixelRatio ||\n this.context.mozBackingStorePixelRatio ||\n this.context.msBackingStorePixelRatio ||\n this.context.oBackingStorePixelRatio ||\n this.context.backingStorePixelRatio ||\n 1;\n\n this.ratio = (window.devicePixelRatio || 1) / backingStore;\n if (this.ratio !== 1) {\n fig.send_message('set_dpi_ratio', { dpi_ratio: this.ratio });\n }\n\n var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n 'canvas'\n ));\n rubberband_canvas.setAttribute(\n 'style',\n 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n );\n\n var resizeObserver = new ResizeObserver(function (entries) {\n var nentries = entries.length;\n for (var i = 0; i < nentries; i++) {\n var entry = entries[i];\n var width, height;\n if (entry.contentBoxSize) {\n if (entry.contentBoxSize instanceof Array) {\n // Chrome 84 implements new version of spec.\n width = entry.contentBoxSize[0].inlineSize;\n height = entry.contentBoxSize[0].blockSize;\n } else {\n // Firefox implements old version of spec.\n width = entry.contentBoxSize.inlineSize;\n height = entry.contentBoxSize.blockSize;\n }\n } else {\n // Chrome <84 implements even older version of spec.\n width = entry.contentRect.width;\n height = entry.contentRect.height;\n }\n\n // Keep the size of the canvas and rubber band canvas in sync with\n // the canvas container.\n if (entry.devicePixelContentBoxSize) {\n // Chrome 84 implements new version of spec.\n canvas.setAttribute(\n 'width',\n entry.devicePixelContentBoxSize[0].inlineSize\n );\n canvas.setAttribute(\n 'height',\n entry.devicePixelContentBoxSize[0].blockSize\n );\n } else {\n canvas.setAttribute('width', width * fig.ratio);\n canvas.setAttribute('height', height * fig.ratio);\n }\n canvas.setAttribute(\n 'style',\n 'width: ' + width + 'px; height: ' + height + 'px;'\n );\n\n rubberband_canvas.setAttribute('width', width);\n rubberband_canvas.setAttribute('height', height);\n\n // And update the size in Python. We ignore the initial 0/0 size\n // that occurs as the element is placed into the DOM, which should\n // otherwise not happen due to the minimum size styling.\n if (width != 0 && height != 0) {\n fig.request_resize(width, height);\n }\n }\n });\n resizeObserver.observe(canvas_div);\n\n function on_mouse_event_closure(name) {\n return function (event) {\n return fig.mouse_event(event, name);\n };\n }\n\n rubberband_canvas.addEventListener(\n 'mousedown',\n on_mouse_event_closure('button_press')\n );\n rubberband_canvas.addEventListener(\n 'mouseup',\n on_mouse_event_closure('button_release')\n );\n // Throttle sequential mouse events to 1 every 20ms.\n rubberband_canvas.addEventListener(\n 'mousemove',\n on_mouse_event_closure('motion_notify')\n );\n\n rubberband_canvas.addEventListener(\n 'mouseenter',\n on_mouse_event_closure('figure_enter')\n );\n rubberband_canvas.addEventListener(\n 'mouseleave',\n on_mouse_event_closure('figure_leave')\n );\n\n canvas_div.addEventListener('wheel', function (event) {\n if (event.deltaY < 0) {\n event.step = 1;\n } else {\n event.step = -1;\n }\n on_mouse_event_closure('scroll')(event);\n });\n\n canvas_div.appendChild(canvas);\n canvas_div.appendChild(rubberband_canvas);\n\n this.rubberband_context = rubberband_canvas.getContext('2d');\n this.rubberband_context.strokeStyle = '#000000';\n\n this._resize_canvas = function (width, height, forward) {\n if (forward) {\n canvas_div.style.width = width + 'px';\n canvas_div.style.height = height + 'px';\n }\n };\n\n // Disable right mouse context menu.\n this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n event.preventDefault();\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\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'mpl-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\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 /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\n continue;\n }\n\n var button = (fig.buttons[name] = document.createElement('button'));\n button.classList = 'mpl-widget';\n button.setAttribute('role', 'button');\n button.setAttribute('aria-disabled', 'false');\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n\n var icon_img = document.createElement('img');\n icon_img.src = '_images/' + image + '.png';\n icon_img.srcset = '_images/' + image + '_large.png 2x';\n icon_img.alt = tooltip;\n button.appendChild(icon_img);\n\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n var fmt_picker = document.createElement('select');\n fmt_picker.classList = 'mpl-widget';\n toolbar.appendChild(fmt_picker);\n this.format_dropdown = fmt_picker;\n\n for (var ind in mpl.extensions) {\n var fmt = mpl.extensions[ind];\n var option = document.createElement('option');\n option.selected = fmt === mpl.default_extension;\n option.innerHTML = fmt;\n fmt_picker.appendChild(option);\n }\n\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n};\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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], msg['forward']);\n fig.send_message('refresh', {});\n }\n};\n\nmpl.figure.prototype.handle_rubberband = function (fig, msg) {\n var x0 = msg['x0'] / fig.ratio;\n var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n var x1 = msg['x1'] / fig.ratio;\n var y1 = (fig.canvas.height - msg['y1']) / fig.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,\n 0,\n fig.canvas.width / fig.ratio,\n fig.canvas.height / fig.ratio\n );\n\n fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n};\n\nmpl.figure.prototype.handle_figure_label = function (fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n};\n\nmpl.figure.prototype.handle_cursor = function (fig, msg) {\n var cursor = msg['cursor'];\n switch (cursor) {\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\nmpl.figure.prototype.handle_message = function (fig, msg) {\n fig.message.textContent = msg['message'];\n};\n\nmpl.figure.prototype.handle_draw = function (fig, _msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n};\n\nmpl.figure.prototype.handle_image_mode = function (fig, msg) {\n fig.image_mode = msg['mode'];\n};\n\nmpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n for (var key in msg) {\n if (!(key in fig.buttons)) {\n continue;\n }\n fig.buttons[key].disabled = !msg[key];\n fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n }\n};\n\nmpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n if (msg['mode'] === 'PAN') {\n fig.buttons['Pan'].classList.add('active');\n fig.buttons['Zoom'].classList.remove('active');\n } else if (msg['mode'] === 'ZOOM') {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.add('active');\n } else {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.remove('active');\n }\n};\n\nmpl.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.\nmpl.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\n fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n evt.data\n );\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n } else if (\n typeof evt.data === 'string' &&\n evt.data.slice(0, 21) === 'data:image/png;base64'\n ) {\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(\n \"No handler for the '\" + msg_type + \"' message type: \",\n msg\n );\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(\n \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n e,\n e.stack,\n msg\n );\n }\n }\n };\n};\n\n// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\nmpl.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 }\n if (e.target) {\n targ = e.target;\n } else if (e.srcElement) {\n targ = e.srcElement;\n }\n if (targ.nodeType === 3) {\n // defeat Safari bug\n targ = targ.parentNode;\n }\n\n // pageX,Y are the mouse positions relative to the document\n var boundingRect = targ.getBoundingClientRect();\n var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n var y = e.pageY - (boundingRect.top + document.body.scrollTop);\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 */\nfunction simpleKeys(original) {\n return Object.keys(original).reduce(function (obj, key) {\n if (typeof original[key] !== 'object') {\n obj[key] = original[key];\n }\n return obj;\n }, {});\n}\n\nmpl.figure.prototype.mouse_event = function (event, name) {\n var canvas_pos = mpl.findpos(event);\n\n if (name === 'button_press') {\n this.canvas.focus();\n this.canvas_div.focus();\n }\n\n var x = canvas_pos.x * this.ratio;\n var y = canvas_pos.y * this.ratio;\n\n this.send_message(name, {\n x: x,\n y: y,\n button: event.button,\n step: event.step,\n guiEvent: simpleKeys(event),\n });\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\nmpl.figure.prototype._key_event_extra = function (_event, _name) {\n // Handle any extra behaviour associated with a key event\n};\n\nmpl.figure.prototype.key_event = function (event, name) {\n // Prevent repeat events\n if (name === 'key_press') {\n if (event.which === this._key) {\n return;\n } else {\n this._key = event.which;\n }\n }\n if (name === 'key_release') {\n this._key = null;\n }\n\n var value = '';\n if (event.ctrlKey && event.which !== 17) {\n value += 'ctrl+';\n }\n if (event.altKey && event.which !== 18) {\n value += 'alt+';\n }\n if (event.shiftKey && event.which !== 16) {\n value += 'shift+';\n }\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, guiEvent: simpleKeys(event) });\n return false;\n};\n\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n\nmpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n\nmpl.default_extension = \"png\";/* global mpl */\n\nvar 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\nmpl.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 = document.getElementById(id);\n var ws_proxy = comm_websocket_adapter(comm);\n\n function ondownload(figure, _format) {\n window.open(figure.canvas.toDataURL());\n }\n\n var fig = new mpl.figure(id, ws_proxy, ondownload, element);\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;\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 fig.cell_info[0].output_area.element.one(\n 'cleared',\n { fig: fig },\n fig._remove_fig_handler\n );\n};\n\nmpl.figure.prototype.handle_close = function (fig, msg) {\n var width = fig.canvas.width / fig.ratio;\n fig.cell_info[0].output_area.element.off(\n 'cleared',\n fig._remove_fig_handler\n );\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.innerHTML =\n '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n fig.close_ws(fig, msg);\n};\n\nmpl.figure.prototype.close_ws = function (fig, msg) {\n fig.send_message('closing', msg);\n // fig.ws.close()\n};\n\nmpl.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 / this.ratio;\n var dataURL = this.canvas.toDataURL();\n this.cell_info[1]['text/html'] =\n '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n};\n\nmpl.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 () {\n fig.push_to_output();\n }, 1000);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'btn-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n var button;\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 /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n continue;\n }\n\n button = fig.buttons[name] = document.createElement('button');\n button.classList = 'btn btn-default';\n button.href = '#';\n button.title = name;\n button.innerHTML = '<i class=\"fa ' + image + ' fa-lg\"></i>';\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n // Add the status bar.\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message pull-right';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n\n // Add the close button to the window.\n var buttongrp = document.createElement('div');\n buttongrp.classList = 'btn-group inline pull-right';\n button = document.createElement('button');\n button.classList = 'btn btn-mini btn-primary';\n button.href = '#';\n button.title = 'Stop Interaction';\n button.innerHTML = '<i class=\"fa fa-power-off icon-remove icon-large\"></i>';\n button.addEventListener('click', function (_evt) {\n fig.handle_close(fig, {});\n });\n button.addEventListener(\n 'mouseover',\n on_mouseover_closure('Stop Interaction')\n );\n buttongrp.appendChild(button);\n var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n titlebar.insertBefore(buttongrp, titlebar.firstChild);\n};\n\nmpl.figure.prototype._remove_fig_handler = function (event) {\n var fig = event.data.fig;\n fig.close_ws(fig, {});\n};\n\nmpl.figure.prototype._root_extra_style = function (el) {\n el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n};\n\nmpl.figure.prototype._canvas_extra_style = function (el) {\n // this is important to make the div 'focusable\n el.setAttribute('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 } else {\n // location in version 2\n IPython.keyboard_manager.register_events(el);\n }\n};\n\nmpl.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\n // Check for shift+enter\n if (event.shiftKey && event.which === 13) {\n this.canvas_div.blur();\n // select the cell after this one\n var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n IPython.notebook.select(index + 1);\n }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n fig.ondownload(fig, null);\n};\n\nmpl.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.\nif (IPython.notebook.kernel !== null) {\n IPython.notebook.kernel.comm_manager.register_target(\n 'matplotlib',\n mpl.mpl_figure_comm\n );\n}\n"
},
"metadata": {}
},
{
"output_type": "display_data",
"data": {
"text/plain": "<IPython.core.display.HTML object>",
"text/html": "<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA+gAAAPoCAYAAABNo9TkAAAgAElEQVR4Xuzdf7D/eV3X/dfuIuwuFAkIUrv8kCB+RDCGrCiugICTgAUkVhCg7NRMjUYqJdM01UwF4aRkXTNNgsEV+AchmApXAoqiDgMUhQRL1IbMgsgCCkrLwgXfveYcL4yF3f2ezzmf5/vxPOd52xlnSj6f1/P5uj8eMa9753z3e8GNN9544/IPAggggAACCCCAAAIIIIAAAghECVxA0KP8DUcAAQQQQAABBBBAAAEEEEDgkABBVwQEEEAAAQQQQAABBBBAAAEEGhAg6A1CsAICCCCAAAIIIIAAAggggAACBF0HEEAAAQQQQAABBBBAAAEEEGhAgKA3CMEKCCCAAAIIIIAAAggggAACCBB0HUAAAQQQQAABBBBAAAEEEECgAQGC3iAEKyCAAAIIIIAAAggggAACCCBA0HUAAQQQQAABBBBAAAEEEEAAgQYECHqDEKyAAAIIIIAAAggggAACCCCAAEHXAQQQQAABBBBAAAEEEEAAAQQaECDoDUKwAgIIIIAAAggggAACCCCAAAIEXQcQQAABBBBAAAEEEEAAAQQQaECAoDcIwQoIIIAAAggggAACCCCAAAIIEHQdQAABBBBAAAEEEEAAAQQQQKABAYLeIAQrIIAAAggggAACCCCAAAIIIEDQdQABBBBAAAEEEEAAAQQQQACBBgQIeoMQrIAAAggggAACCCCAAAIIIIAAQdcBBBBAAAEEEEAAAQQQQAABBBoQIOgNQrACAggggAACCCCAAAIIIIAAAgRdBxBAAAEEEEAAAQQQQAABBBBoQICgNwjBCggggAACCCCAAAIIIIAAAggQdB1AAAEEEEAAAQQQQAABBBBAoAEBgt4gBCsggAACCCCAAAIIIIAAAgggQNB1AAEEEEAAAQQQQAABBBBAAIEGBAh6gxCsgAACCCCAAAIIIIAAAggggABB1wEEEEAAAQQQQAABBBBAAAEEGhAg6A1CsAICCCCAAAIIIIAAAggggAACBF0HEEAAAQQQQAABBBBAAAEEEGhAgKA3CMEKCCCAAAIIIIAAAggggAACCBB0HUAAAQQQQAABBBBAAAEEEECgAQGC3iAEKyCAAAIIIIAAAggggAACCCBA0HUAAQQQQAABBBBAAAEEEEAAgQYECHqDEKyAAAIIIIAAAggggAACCCCAAEHXAQQQQAABBBBAAAEEEEAAAQQaECDoDUKwAgIIIIAAAggggAACCCCAAAIEXQcQQAABBBBAAAEEEEAAAQQQaECAoDcIwQoIIIAAAggggAACCCCAAAIIEHQdQAABBBBAAAEEEEAAAQQQQKABAYLeIAQrIIAAAggggAACCCCAAAIIIEDQdQABBBBAAAEEEEAAAQQQQACBBgQIeoMQrIAAAggggAACCCCAAAIIIIAAQdcBBBBAAAEEEEAAAQQQQAABBBoQIOgNQrACAggggAACCCCAAAIIIIAAAgRdBxBAAAEEEEAAAQQQQAABBBBoQICgNwjBCggggAACCCCAAAIIIIAAAggQdB1AAAEEEEAAAQQQQAABBBBAoAEBgt4gBCsggAACCCCAAAIIIIAAAgggQNB1AAEEEEAAAQQQQAABBBBAAIEGBAh6gxCsgAACCCCAAAIIIIAAAggggABB1wEEEEAAAQQQQAABBBBAAAEEGhAg6A1CsAICCCCAAAIIIIAAAggggAACBF0HEEAAAQQQQAABBBBAAAEEEGhAgKA3CMEKCCCAAAIIIIAAAggggAACCBB0HUAAAQQQQAABBBBAAAEEEECgAQGC3iAEKyCAAAIIIIAAAggggAACCCBA0HUAAQQQQAABBBBAAAEEEEAAgQYECHqDEKyAAAIIIIAAAggggAACCCCAAEHXAQQQQAABBBBAAAEEEEAAAQQaECDoDUKwAgIIIIAAAggggAACCCCAAAIEXQcQQAABBBBAAAEEEEAAAQQQaECAoDcIwQoIIIAAAggggAACCCCAAAIIEHQdQAABBBBAAAEEEEAAAQQQQKABAYLeIAQrIIAAAggggAACCCCAAAIIIEDQdQABBBBAAAEEEEAAAQQQQACBBgQIeoMQrIAAAggggAACCCCAAAIIIIAAQdcBBBBAAAEEEEAAAQQQQAABBBoQIOgNQrACAggggAACCCCAAAIIIIAAAgRdBxBAAAEEEEAAAQQQQAABBBBoQICgNwjBCggggAACCCCAAAIIIIAAAggQdB1AAAEEEEAAAQQQQAABBBBAoAEBgt4gBCsggAACCCCAAAIIIIAAAgggQNB1AAEEEEAAAQQQQAABBBBAAIEGBAh6gxCsgAACCCCAAAIIIIAAAggggABB1wEEEEAAAQQQQAABBBBAAAEEGhAg6A1CsAICCCCAAAIIIIAAAggggAACBF0HEEAAAQQQQAABBBBAAAEEEGhAgKA3CMEKCCCAAAIIIIAAAggggAACCBB0HUAAAQQQQAABBBBAAAEEEECgAQGC3iAEKyCAAAIIIIAAAggggAACCCBA0HUAAQQQQAABBBBAAAEEEEAAgQYECHqDEKyAAAIIIIAAAggggAACCCCAAEHXAQQQQAABBBBAAAEEEEAAAQQaECDoDUKwAgIIIIAAAggggAACCCCAAAIEXQcQQAABBBBAAAEEEEAAAQQQaECAoDcIwQoIIIAAAggggAACCCCAAAIIEHQdQAABBBBAAAEEEEAAAQQQQKABAYLeIAQrIIAAAggggAACCCCAAAIIIEDQdQABBBBAAAEEEEAAAQQQQACBBgQIeoMQrIAAAggggAACCCCAAAIIIIAAQdcBBBBAAAEEEEAAAQQQQAABBBoQIOgNQrACAggggAACCCCAAAIIIIAAAgRdBxBAAAEEEEAAAQQQQAABBBBoQICgNwjBCggggAACCCCAAAIIIIAAAggQdB1AAAEEEEAAAQQQQAABBBBAoAEBgt4gBCsggAACCCCAAAIIIIAAAgggQNB1AAEEEEAAAQQQQAABBBBAAIEGBAh6gxCsgAACCCCAAAIIIIAAAggggABB1wEEEEAAAQQQQAABBBBAAAEEGhAg6A1CsAICCCCAAAIIIIAAAggggAACBF0HEEAAAQQQQAABBBBAAAEEEGhAgKA3CMEKCCCAAAIIIIAAAggggAACCBB0HUAAAQQQQAABBBBAAAEEEECgAQGC3iAEKyCAAAIIIIAAAggggAACCCBA0HUAAQQQQAABBBBAAAEEEEAAgQYECHqDEKyAAAIIIIAAAggggAACCCCAAEHXAQQQQAABBBBAAAEEEEAAAQQaECDoDUKwAgIIIIAAAggggAACCCCAAAIEXQcQQAABBBBAAAEEEEAAAQQQaECAoDcIwQoIIIAAAggggAACCCCAAAIIEPQGHfg7f+fvrB/5kR/5w03e/OY3r0c96lENNrMCAggggAACCCCAAAIIIIDAVgQI+lakb2HOu971rvWwhz1sff7znyfo4SyMRwABBBBAAAEEEEAAAQSSBAh6kP65c+fWN37jN653vOMd6653veu67rrrDrfxE/RgKEYjgAACCCCAAAIIIIAAAiECBD0E/mDsi1/84vW3//bfXve///3Xk5/85PWCF7yAoAfzMBoBBBBAAAEEEEAAAQQQSBIg6CH611577XrgAx+4Pv3pTx/+xPyXf/mX1z/6R/+IoIfyMBYBBBBAAAEEEEAAAQQQSBMg6KEEnvSkJ62f//mfX8961rPWy172svUP/+E/JOihLIxFAAEEEEAAAQQQQAABBDoQIOiBFF71qlet7/7u7153utOd1vve9771NV/zNQQ9kIORCCCAAAIIIIAAAggggEAnAgR94zQ++clPrgc84AHrt3/7t9dP/MRPrKuuuupwAz9B3zgI4xBAAAEEEEAAAQQQQACBZgQI+saB/LW/9tcOxfybvumb1q/92q+tCy64YG+C/qEPfehWb3PDDTcc/sT+bne72+FP7W9zm9tsfHvjEEAAAQQQQAABBBBAoAOBg7/m+WMf+9jhKg9+8IPXxRdf3GGt8TsQ9A0rcCDkV1555brooovWO9/5zsP/h/DFf/bxE/Qvyv6GVzIKAQQQQAABBBBAAAEETjmBt7/97esbvuEbTvktzsb6BH2jHD/3uc+thz70oevqq69ez3ve89aLXvSim0wm6BsFYQwCCCCAAAIIIIAAAgjchABB71MIgr5RFl8U8Hvc4x7rve9977r97W+/d0E/36+4H/zVbge/Wn/wzxvf+MZ1z3vec6PbGzONwGc/+9n1tre97fDaV1xxxbrd7W43DYH7bkRA1zYCbczSNSXYioCubUXanA9+8IPrcY973CGID3zgA+te97oXKA0IEPQNQjj4c98PechD1sFP0f/Df/gP6zu/8zu/Yuo+foJ+vqscCPzll19++LH3v//96773ve/5vuI/R+BYBD7zmc+sN7zhDYffffzjH78uueSSY53jSwicj4CunY+Q/3xfBHRtXySdcz4CunY+Qv7zfRH4H//jf6z73e9+h8cd/CDvsssu29fRzjkBAYJ+AnhH/epf/+t/ff2bf/Nv1td93detf/JP/snNfu3Vr371+umf/unD/+zv//2/vx74wAce/p8P/r70L/9p+1HnfvnnCPpxyfnergQ8LnYl5vPHJaBrxyXne7sS0LVdifn8cQno2nHJ+d6uBAj6rsS2+TxB34Dzs5/97PXyl7/8WJP2+esmBP1YEfjSMQh4XBwDmq8ci4CuHQubLx2DgK4dA5qvHIuArh0Lmy8dgwBBPwa0Db5C0DeA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\" width=\"500\">"
},
"metadata": {}
},
{
"output_type": "execute_result",
"execution_count": 3,
"data": {
"text/plain": "<matplotlib.collections.PathCollection at 0x7fc4894279d0>"
},
"metadata": {}
}
]
},
{
"metadata": {
"ExecuteTime": {
"start_time": "2021-03-28T06:13:55.767976Z",
"end_time": "2021-03-28T06:13:55.777619Z"
},
"code_folding": [],
"trusted": true
},
"cell_type": "code",
"source": "# FABRIK: Backward + Forward\n\ndef FABRIK(arm, _tx, _ty):\n tx = _tx\n ty = _ty\n for i in reversed(range(N)):\n arm[i].backward(tx, ty)\n tx = arm[i].ax\n ty = arm[i].ay\n \n baseX = OX\n baseY = OY\n for i in range(N):\n arm[i].forward(baseX, baseY);\n baseX = arm[i].bx\n baseY = arm[i].by\n \n PX = []\n PY = []\n for i in range(N):\n PX.append(arm[i].ax)\n PY.append(arm[i].ay) \n PX.append(arm[N-1].bx)\n PY.append(arm[N-1].by)\n \n return PX, PY",
"execution_count": 4,
"outputs": []
},
{
"metadata": {
"ExecuteTime": {
"start_time": "2021-03-28T06:13:55.791212Z",
"end_time": "2021-03-28T06:13:55.911454Z"
},
"code_folding": [],
"trusted": true
},
"cell_type": "code",
"source": "def motion(event):\n mx = event.xdata\n my = event.ydata\n Mouse.set_data(mx, my)\n \n FX, FY = FABRIK(fab, mx, my)\n FLine.set_data(FX, FY)\n FDot.set_data(FX, FY)\n \n plt.draw()\n\nfig = plt.figure(figsize=(5,5))\nax = fig.add_subplot(111)\nax.axis([-1,N,-1,N])\nax.grid()\n\nFLine, = ax.plot([],[], linestyle='-', color='orange', alpha=1, label='FABRIK')\nFDot, = ax.plot([],[], marker='o', color='orange')\nMouse, = ax.plot([],[], marker='x', ms=20, color='red')\n\nplt.connect('motion_notify_event', motion)\nplt.legend(loc='lower right')\nplt.show()",
"execution_count": 5,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/plain": "<IPython.core.display.Javascript object>",
"application/javascript": "/* Put everything inside the global mpl namespace */\n/* global mpl */\nwindow.mpl = {};\n\nmpl.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(\n '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\nmpl.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 = document.createElement('div');\n this.root.setAttribute('style', 'display: inline-block');\n this._root_extra_style(this.root);\n\n parent_element.appendChild(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 (fig.ratio !== 1) {\n fig.send_message('set_dpi_ratio', { dpi_ratio: fig.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\nmpl.figure.prototype._init_header = function () {\n var titlebar = document.createElement('div');\n titlebar.classList =\n 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n var titletext = document.createElement('div');\n titletext.classList = 'ui-dialog-title';\n titletext.setAttribute(\n 'style',\n 'width: 100%; text-align: center; padding: 3px;'\n );\n titlebar.appendChild(titletext);\n this.root.appendChild(titlebar);\n this.header = titletext;\n};\n\nmpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._init_canvas = function () {\n var fig = this;\n\n var canvas_div = (this.canvas_div = document.createElement('div'));\n canvas_div.setAttribute(\n 'style',\n 'border: 1px solid #ddd;' +\n 'box-sizing: content-box;' +\n 'clear: both;' +\n 'min-height: 1px;' +\n 'min-width: 1px;' +\n 'outline: 0;' +\n 'overflow: hidden;' +\n 'position: relative;' +\n 'resize: both;'\n );\n\n function on_keyboard_event_closure(name) {\n return function (event) {\n return fig.key_event(event, name);\n };\n }\n\n canvas_div.addEventListener(\n 'keydown',\n on_keyboard_event_closure('key_press')\n );\n canvas_div.addEventListener(\n 'keyup',\n on_keyboard_event_closure('key_release')\n );\n\n this._canvas_extra_style(canvas_div);\n this.root.appendChild(canvas_div);\n\n var canvas = (this.canvas = document.createElement('canvas'));\n canvas.classList.add('mpl-canvas');\n canvas.setAttribute('style', 'box-sizing: content-box;');\n\n this.context = canvas.getContext('2d');\n\n var backingStore =\n this.context.backingStorePixelRatio ||\n this.context.webkitBackingStorePixelRatio ||\n this.context.mozBackingStorePixelRatio ||\n this.context.msBackingStorePixelRatio ||\n this.context.oBackingStorePixelRatio ||\n this.context.backingStorePixelRatio ||\n 1;\n\n this.ratio = (window.devicePixelRatio || 1) / backingStore;\n if (this.ratio !== 1) {\n fig.send_message('set_dpi_ratio', { dpi_ratio: this.ratio });\n }\n\n var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n 'canvas'\n ));\n rubberband_canvas.setAttribute(\n 'style',\n 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n );\n\n var resizeObserver = new ResizeObserver(function (entries) {\n var nentries = entries.length;\n for (var i = 0; i < nentries; i++) {\n var entry = entries[i];\n var width, height;\n if (entry.contentBoxSize) {\n if (entry.contentBoxSize instanceof Array) {\n // Chrome 84 implements new version of spec.\n width = entry.contentBoxSize[0].inlineSize;\n height = entry.contentBoxSize[0].blockSize;\n } else {\n // Firefox implements old version of spec.\n width = entry.contentBoxSize.inlineSize;\n height = entry.contentBoxSize.blockSize;\n }\n } else {\n // Chrome <84 implements even older version of spec.\n width = entry.contentRect.width;\n height = entry.contentRect.height;\n }\n\n // Keep the size of the canvas and rubber band canvas in sync with\n // the canvas container.\n if (entry.devicePixelContentBoxSize) {\n // Chrome 84 implements new version of spec.\n canvas.setAttribute(\n 'width',\n entry.devicePixelContentBoxSize[0].inlineSize\n );\n canvas.setAttribute(\n 'height',\n entry.devicePixelContentBoxSize[0].blockSize\n );\n } else {\n canvas.setAttribute('width', width * fig.ratio);\n canvas.setAttribute('height', height * fig.ratio);\n }\n canvas.setAttribute(\n 'style',\n 'width: ' + width + 'px; height: ' + height + 'px;'\n );\n\n rubberband_canvas.setAttribute('width', width);\n rubberband_canvas.setAttribute('height', height);\n\n // And update the size in Python. We ignore the initial 0/0 size\n // that occurs as the element is placed into the DOM, which should\n // otherwise not happen due to the minimum size styling.\n if (width != 0 && height != 0) {\n fig.request_resize(width, height);\n }\n }\n });\n resizeObserver.observe(canvas_div);\n\n function on_mouse_event_closure(name) {\n return function (event) {\n return fig.mouse_event(event, name);\n };\n }\n\n rubberband_canvas.addEventListener(\n 'mousedown',\n on_mouse_event_closure('button_press')\n );\n rubberband_canvas.addEventListener(\n 'mouseup',\n on_mouse_event_closure('button_release')\n );\n // Throttle sequential mouse events to 1 every 20ms.\n rubberband_canvas.addEventListener(\n 'mousemove',\n on_mouse_event_closure('motion_notify')\n );\n\n rubberband_canvas.addEventListener(\n 'mouseenter',\n on_mouse_event_closure('figure_enter')\n );\n rubberband_canvas.addEventListener(\n 'mouseleave',\n on_mouse_event_closure('figure_leave')\n );\n\n canvas_div.addEventListener('wheel', function (event) {\n if (event.deltaY < 0) {\n event.step = 1;\n } else {\n event.step = -1;\n }\n on_mouse_event_closure('scroll')(event);\n });\n\n canvas_div.appendChild(canvas);\n canvas_div.appendChild(rubberband_canvas);\n\n this.rubberband_context = rubberband_canvas.getContext('2d');\n this.rubberband_context.strokeStyle = '#000000';\n\n this._resize_canvas = function (width, height, forward) {\n if (forward) {\n canvas_div.style.width = width + 'px';\n canvas_div.style.height = height + 'px';\n }\n };\n\n // Disable right mouse context menu.\n this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n event.preventDefault();\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\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'mpl-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\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 /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\n continue;\n }\n\n var button = (fig.buttons[name] = document.createElement('button'));\n button.classList = 'mpl-widget';\n button.setAttribute('role', 'button');\n button.setAttribute('aria-disabled', 'false');\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n\n var icon_img = document.createElement('img');\n icon_img.src = '_images/' + image + '.png';\n icon_img.srcset = '_images/' + image + '_large.png 2x';\n icon_img.alt = tooltip;\n button.appendChild(icon_img);\n\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n var fmt_picker = document.createElement('select');\n fmt_picker.classList = 'mpl-widget';\n toolbar.appendChild(fmt_picker);\n this.format_dropdown = fmt_picker;\n\n for (var ind in mpl.extensions) {\n var fmt = mpl.extensions[ind];\n var option = document.createElement('option');\n option.selected = fmt === mpl.default_extension;\n option.innerHTML = fmt;\n fmt_picker.appendChild(option);\n }\n\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n};\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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], msg['forward']);\n fig.send_message('refresh', {});\n }\n};\n\nmpl.figure.prototype.handle_rubberband = function (fig, msg) {\n var x0 = msg['x0'] / fig.ratio;\n var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n var x1 = msg['x1'] / fig.ratio;\n var y1 = (fig.canvas.height - msg['y1']) / fig.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,\n 0,\n fig.canvas.width / fig.ratio,\n fig.canvas.height / fig.ratio\n );\n\n fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n};\n\nmpl.figure.prototype.handle_figure_label = function (fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n};\n\nmpl.figure.prototype.handle_cursor = function (fig, msg) {\n var cursor = msg['cursor'];\n switch (cursor) {\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\nmpl.figure.prototype.handle_message = function (fig, msg) {\n fig.message.textContent = msg['message'];\n};\n\nmpl.figure.prototype.handle_draw = function (fig, _msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n};\n\nmpl.figure.prototype.handle_image_mode = function (fig, msg) {\n fig.image_mode = msg['mode'];\n};\n\nmpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n for (var key in msg) {\n if (!(key in fig.buttons)) {\n continue;\n }\n fig.buttons[key].disabled = !msg[key];\n fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n }\n};\n\nmpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n if (msg['mode'] === 'PAN') {\n fig.buttons['Pan'].classList.add('active');\n fig.buttons['Zoom'].classList.remove('active');\n } else if (msg['mode'] === 'ZOOM') {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.add('active');\n } else {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.remove('active');\n }\n};\n\nmpl.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.\nmpl.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\n fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n evt.data\n );\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n } else if (\n typeof evt.data === 'string' &&\n evt.data.slice(0, 21) === 'data:image/png;base64'\n ) {\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(\n \"No handler for the '\" + msg_type + \"' message type: \",\n msg\n );\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(\n \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n e,\n e.stack,\n msg\n );\n }\n }\n };\n};\n\n// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\nmpl.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 }\n if (e.target) {\n targ = e.target;\n } else if (e.srcElement) {\n targ = e.srcElement;\n }\n if (targ.nodeType === 3) {\n // defeat Safari bug\n targ = targ.parentNode;\n }\n\n // pageX,Y are the mouse positions relative to the document\n var boundingRect = targ.getBoundingClientRect();\n var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n var y = e.pageY - (boundingRect.top + document.body.scrollTop);\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 */\nfunction simpleKeys(original) {\n return Object.keys(original).reduce(function (obj, key) {\n if (typeof original[key] !== 'object') {\n obj[key] = original[key];\n }\n return obj;\n }, {});\n}\n\nmpl.figure.prototype.mouse_event = function (event, name) {\n var canvas_pos = mpl.findpos(event);\n\n if (name === 'button_press') {\n this.canvas.focus();\n this.canvas_div.focus();\n }\n\n var x = canvas_pos.x * this.ratio;\n var y = canvas_pos.y * this.ratio;\n\n this.send_message(name, {\n x: x,\n y: y,\n button: event.button,\n step: event.step,\n guiEvent: simpleKeys(event),\n });\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\nmpl.figure.prototype._key_event_extra = function (_event, _name) {\n // Handle any extra behaviour associated with a key event\n};\n\nmpl.figure.prototype.key_event = function (event, name) {\n // Prevent repeat events\n if (name === 'key_press') {\n if (event.which === this._key) {\n return;\n } else {\n this._key = event.which;\n }\n }\n if (name === 'key_release') {\n this._key = null;\n }\n\n var value = '';\n if (event.ctrlKey && event.which !== 17) {\n value += 'ctrl+';\n }\n if (event.altKey && event.which !== 18) {\n value += 'alt+';\n }\n if (event.shiftKey && event.which !== 16) {\n value += 'shift+';\n }\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, guiEvent: simpleKeys(event) });\n return false;\n};\n\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n\nmpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n\nmpl.default_extension = \"png\";/* global mpl */\n\nvar 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\nmpl.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 = document.getElementById(id);\n var ws_proxy = comm_websocket_adapter(comm);\n\n function ondownload(figure, _format) {\n window.open(figure.canvas.toDataURL());\n }\n\n var fig = new mpl.figure(id, ws_proxy, ondownload, element);\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;\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 fig.cell_info[0].output_area.element.one(\n 'cleared',\n { fig: fig },\n fig._remove_fig_handler\n );\n};\n\nmpl.figure.prototype.handle_close = function (fig, msg) {\n var width = fig.canvas.width / fig.ratio;\n fig.cell_info[0].output_area.element.off(\n 'cleared',\n fig._remove_fig_handler\n );\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.innerHTML =\n '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n fig.close_ws(fig, msg);\n};\n\nmpl.figure.prototype.close_ws = function (fig, msg) {\n fig.send_message('closing', msg);\n // fig.ws.close()\n};\n\nmpl.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 / this.ratio;\n var dataURL = this.canvas.toDataURL();\n this.cell_info[1]['text/html'] =\n '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n};\n\nmpl.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 () {\n fig.push_to_output();\n }, 1000);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'btn-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n var button;\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 /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n continue;\n }\n\n button = fig.buttons[name] = document.createElement('button');\n button.classList = 'btn btn-default';\n button.href = '#';\n button.title = name;\n button.innerHTML = '<i class=\"fa ' + image + ' fa-lg\"></i>';\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n // Add the status bar.\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message pull-right';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n\n // Add the close button to the window.\n var buttongrp = document.createElement('div');\n buttongrp.classList = 'btn-group inline pull-right';\n button = document.createElement('button');\n button.classList = 'btn btn-mini btn-primary';\n button.href = '#';\n button.title = 'Stop Interaction';\n button.innerHTML = '<i class=\"fa fa-power-off icon-remove icon-large\"></i>';\n button.addEventListener('click', function (_evt) {\n fig.handle_close(fig, {});\n });\n button.addEventListener(\n 'mouseover',\n on_mouseover_closure('Stop Interaction')\n );\n buttongrp.appendChild(button);\n var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n titlebar.insertBefore(buttongrp, titlebar.firstChild);\n};\n\nmpl.figure.prototype._remove_fig_handler = function (event) {\n var fig = event.data.fig;\n fig.close_ws(fig, {});\n};\n\nmpl.figure.prototype._root_extra_style = function (el) {\n el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n};\n\nmpl.figure.prototype._canvas_extra_style = function (el) {\n // this is important to make the div 'focusable\n el.setAttribute('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 } else {\n // location in version 2\n IPython.keyboard_manager.register_events(el);\n }\n};\n\nmpl.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\n // Check for shift+enter\n if (event.shiftKey && event.which === 13) {\n this.canvas_div.blur();\n // select the cell after this one\n var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n IPython.notebook.select(index + 1);\n }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n fig.ondownload(fig, null);\n};\n\nmpl.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.\nif (IPython.notebook.kernel !== null) {\n IPython.notebook.kernel.comm_manager.register_target(\n 'matplotlib',\n mpl.mpl_figure_comm\n );\n}\n"
},
"metadata": {}
},
{
"output_type": "display_data",
"data": {
"text/plain": "<IPython.core.display.HTML object>",
"text/html": "<img src=\"data:,\" width=\"0\">"
},
"metadata": {}
}
]
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2021-03-13T03:40:42.221300Z",
"start_time": "2021-03-13T03:40:42.214212Z"
},
"trusted": true
},
"cell_type": "code",
"source": "",
"execution_count": null,
"outputs": []
}
],
"metadata": {
"_draft": {
"nbviewer_url": "https://gist.github.com/b21087835d726439bbdf08b42e73470e"
},
"gist": {
"id": "b21087835d726439bbdf08b42e73470e",
"data": {
"description": "IK_FABRIK",
"public": true
}
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3",
"language": "python"
},
"language_info": {
"name": "python",
"version": "3.8.5",
"mimetype": "text/x-python",
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"pygments_lexer": "ipython3",
"nbconvert_exporter": "python",
"file_extension": ".py"
},
"nteract": {
"version": "0.28.0"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment