Skip to content

Instantly share code, notes, and snippets.

@mirrornerror
Last active April 30, 2021 10:09
Show Gist options
  • Select an option

  • Save mirrornerror/156ab9d22aec5a57fe7da584ba371ebe to your computer and use it in GitHub Desktop.

Select an option

Save mirrornerror/156ab9d22aec5a57fe7da584ba371ebe to your computer and use it in GitHub Desktop.
IK_CCD_AngleLimit
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"metadata": {},
"cell_type": "markdown",
"source": "## Inverse Kinematics: CCD(Cyclic Coodinate Descent)"
},
{
"metadata": {
"ExecuteTime": {
"start_time": "2021-04-12T08:31:33.256639Z",
"end_time": "2021-04-12T08:31:35.073213Z"
},
"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": 3,
"outputs": []
},
{
"metadata": {
"ExecuteTime": {
"start_time": "2021-04-12T08:34:25.320741Z",
"end_time": "2021-04-12T08:34:25.325567Z"
},
"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)",
"execution_count": 8,
"outputs": []
},
{
"metadata": {
"ExecuteTime": {
"start_time": "2021-04-12T08:34:58.261800Z",
"end_time": "2021-04-12T08:34:58.315447Z"
},
"trusted": true
},
"cell_type": "code",
"source": "N = 4 # the number of link\nOX = 0 # offset x\nOY = 0 # offset y\n\nccd = []\nfor i in range(N):\n linkLength = 1 - i * 0.1\n initTheta = np.pi / 2 # vertical position\n if i == 0:\n ccd.append(Arm(OX, OY, linkLength, initTheta))\n else:\n ccd.append(Arm(ccd[i-1].bx, ccd[i-1].by, linkLength, initTheta))\n \nccdX = []\nccdY = []\nfor i in range(N):\n ccdX.append([ccd[i].ax, ccd[i].bx])\n ccdY.append([ccd[i].ay, ccd[i].by])\n \nfig = plt.figure(figsize=(5,5))\nax = fig.add_subplot(111)\nax.axis([-1,N,-1,N])\nax.grid()\nax.plot(ccdX, ccdY, color='magenta')\nax.scatter(ccdX, ccdY, color='magenta')",
"execution_count": 9,
"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,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6X/XX399++IXvzhts++++z74zwu6nrHnROBLX/pSO/fcc9vFF1/c+j/vtNNObfPmze3oo49uJ510Uttll13mNIk2G5HAV7/61W1+R/XfU3feeee06mtf+9p2wQUXbMS17RRAoP95+Dd/8zft4x//ePvMZz7Tuvd22GGH9sM//MPtoIMOam94wxvaT/7kTwZMpuVGInD33Xe3Sy65ZDpz3XDDDe1f/uVf2te+9rV23333td13370997nPbT/zMz8z+W3PPffcSKvbJRmBU045pZ199tkPTnX55Ze3LVu2JJuyxjgCeg2dV7TloYce2q644oolHyugrwhh+Qf1UH7MMce0u+66a0kWz372s6eDyH777VeeFQBrI7Dddts96hMF9LUx9axHEnjpS1/arrrqqmXRHHfcce2d73xn23HHHZd9rAcgsBSBj33sY+2www5bFs4Tn/jE9qd/+qftp37qp5Z9rAcgsFoCn/rUp9qLXvSidv/99wvoq4U34PEC+gCoi1qy/y3ZlVdeOY2/xx57TP+hXnPNNa2/si6gL6qq85u7/3Lvryrde++9bdOmTe3UU09t/S99+qsAF154YXvHO94xDfOc5zxneqWgP8aFwGoJPDSgP/WpT20/8iM/0i699NKpjIC+Wpoe/2gE+l0/t9566/Rq+VFHHTW9Uv60pz2tfe9735v+XPyDP/iD6ZXOfv3CL/xCe9/73gcmAmsi0AP6L/3SL01/Xv74j/9467/X9t577/bAAw+0L3/5y+0DH/hA+9CHPjR5r/9FUP/z80d/9EfX1MuTEFiKQPfaS17ykslbT37yk6e7hfrlFfQ4vwjocezTdT7//POn0LT//vtPtyT36+lPf3q77bbbBPR0auUbaOsdGNtvv/30ytOBBx64zZD9tql++1S/3vrWt7Y3v/nN+ZYwUXoCp59+envxi188/W+vvfaa3nrzjGc8Q0BPr9xiDXjkkUe2448/vr361a9uP/RDP/SI4b/+9a+3gw8+uH3+85+fftZ/57ndfbE0zjJtD95Leeyh8/3FX/xFe+UrXzn9q1e96lXtgx/8YJbxzbEBCLz97W9vv/EbvzG9gNJ9duaZZwrowboK6MECZG8voGdXKMd8/W9d+1/s9OvEE09s55133iMG639D+7znPa/dfPPN0x0ad9xxx/R+ThcC6yEgoK+Hnueuh8BHPvKR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\" width=\"500\">"
},
"metadata": {}
},
{
"output_type": "execute_result",
"execution_count": 9,
"data": {
"text/plain": "<matplotlib.collections.PathCollection at 0x7f7eeb6c8310>"
},
"metadata": {}
}
]
},
{
"metadata": {
"ExecuteTime": {
"start_time": "2021-04-12T08:41:59.040115Z",
"end_time": "2021-04-12T08:41:59.052298Z"
},
"code_folding": [],
"trusted": true
},
"cell_type": "code",
"source": "# Inverse Kinematiks: CCD\n\ndef CCD(arm, tx, ty):\n for i in reversed(range(N)):\n targetTheta = np.arctan2(ty - arm[i].ay, tx - arm[i].ax)\n tipTheta = np.arctan2(arm[-1].by - arm[i].ay, arm[-1].bx - arm[i].ax)\n theta = targetTheta - tipTheta;\n for j in range(i, N):\n linkTheta = np.arctan2(arm[j].by - arm[i].ay, arm[j].bx - arm[i].ax)\n linkDist = ((arm[j].bx - arm[i].ax)**2 + (arm[j].by - arm[i].ay)**2)**0.5\n arm[j].bx = arm[i].ax + linkDist * np.cos(theta + linkTheta)\n arm[j].by = arm[i].ay + linkDist * np.sin(theta + linkTheta)\n if j < N - 1:\n arm[j+1].ax = arm[j].bx\n arm[j+1].ay = arm[j].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\n ",
"execution_count": 12,
"outputs": []
},
{
"metadata": {
"ExecuteTime": {
"start_time": "2021-04-12T08:42:05.118580Z",
"end_time": "2021-04-12T08:42:05.167927Z"
},
"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 CX, CY = CCD(ccd, mx, my)\n CLine.set_data(CX, CY)\n CDot.set_data(CX, CY)\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\nCLine, = ax.plot([],[], linestyle='-', color='magenta', alpha=1, label='CCD')\nCDot, = ax.plot([],[], marker='o', color='magenta')\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": 13,
"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": {
"trusted": true
},
"cell_type": "code",
"source": "",
"execution_count": null,
"outputs": []
}
],
"metadata": {
"_draft": {
"nbviewer_url": "https://gist.github.com/156ab9d22aec5a57fe7da584ba371ebe"
},
"gist": {
"id": "156ab9d22aec5a57fe7da584ba371ebe",
"data": {
"description": "IK_CCD",
"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