1{ 2 "cells": [ 3 { 4 "cell_type": "code", 5 "execution_count": 1, 6 "metadata": {}, 7 "outputs": [], 8 "source": [ 9 "# core stuff\n", 10 "import numpy as np\n", 11 "import pickle as pk\n", 12 "from copy import deepcopy\n", 13 "\n", 14 "# plotting stuff\n", 15 "import matplotlib.pyplot as plt\n", 16 "from mpl_toolkits import mplot3d\n", 17 "from matplotlib import rc\n", 18 "rc('font',**{'family':'sans-serif','sans-serif':['Helvetica']})\n", 19 "## for Palatino and other serif fonts use:\n", 20 "#rc('font',**{'family':'serif','serif':['Palatino']})\n", 21 "rc('text', usetex=True)\n", 22 "%matplotlib notebook\n", 23 "\n", 24 "# Ensure that changes in imported module (gravann most importantly) are autoreloaded\n", 25 "%load_ext autoreload\n", 26 "%autoreload 2\n" 27 ] 28 }, 29 { 30 "cell_type": "code", 31 "execution_count": 2, 32 "metadata": {}, 33 "outputs": [], 34 "source": [ 35 "# Load all data (produced using apophis.cpp benchmark)\n", 36 "with open(\"data/apophis_err.pk\", \"rb\") as file:\n", 37 " err_apophis = pk.load(file)\n", 38 "err_apophis[:,0] = err_apophis[:,0] / 60 / 60 / 24 / 365.25" 39 ] 40 }, 41 { 42 "cell_type": "code", 43 "execution_count": 27, 44 "metadata": {}, 45 "outputs": [ 46 { 47 "data": { 48 "application/javascript": [ 49 "/* Put everything inside the global mpl namespace */\n", 50 "/* global mpl */\n", 51 "window.mpl = {};\n", 52 "\n", 53 "mpl.get_websocket_type = function () {\n", 54 " if (typeof WebSocket !== 'undefined') {\n", 55 " return WebSocket;\n", 56 " } else if (typeof MozWebSocket !== 'undefined') {\n", 57 " return MozWebSocket;\n", 58 " } else {\n", 59 " alert(\n", 60 " 'Your browser does not have WebSocket support. ' +\n", 61 " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", 62 " 'Firefox 4 and 5 are also supported but you ' +\n", 63 " 'have to enable WebSockets in about:config.'\n", 64 " );\n", 65 " }\n", 66 "};\n", 67 "\n", 68 "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", 69 " this.id = figure_id;\n", 70 "\n", 71 " this.ws = websocket;\n", 72 "\n", 73 " this.supports_binary = this.ws.binaryType !== undefined;\n", 74 "\n", 75 " if (!this.supports_binary) {\n", 76 " var warnings = document.getElementById('mpl-warnings');\n", 77 " if (warnings) {\n", 78 " warnings.style.display = 'block';\n", 79 " warnings.textContent =\n", 80 " 'This browser does not support binary websocket messages. ' +\n", 81 " 'Performance may be slow.';\n", 82 " }\n", 83 " }\n", 84 "\n", 85 " this.imageObj = new Image();\n", 86 "\n", 87 " this.context = undefined;\n", 88 " this.message = undefined;\n", 89 " this.canvas = undefined;\n", 90 " this.rubberband_canvas = undefined;\n", 91 " this.rubberband_context = undefined;\n", 92 " this.format_dropdown = undefined;\n", 93 "\n", 94 " this.image_mode = 'full';\n", 95 "\n", 96 " this.root = document.createElement('div');\n", 97 " this.root.setAttribute('style', 'display: inline-block');\n", 98 " this._root_extra_style(this.root);\n", 99 "\n", 100 " parent_element.appendChild(this.root);\n", 101 "\n", 102 " this._init_header(this);\n", 103 " this._init_canvas(this);\n", 104 " this._init_toolbar(this);\n", 105 "\n", 106 " var fig = this;\n", 107 "\n", 108 " this.waiting = false;\n", 109 "\n", 110 " this.ws.onopen = function () {\n", 111 " fig.send_message('supports_binary', { value: fig.supports_binary });\n", 112 " fig.send_message('send_image_mode', {});\n", 113 " if (fig.ratio !== 1) {\n", 114 " fig.send_message('set_dpi_ratio', { dpi_ratio: fig.ratio });\n", 115 " }\n", 116 " fig.send_message('refresh', {});\n", 117 " };\n", 118 "\n", 119 " this.imageObj.onload = function () {\n", 120 " if (fig.image_mode === 'full') {\n", 121 " // Full images could contain transparency (where diff images\n", 122 " // almost always do), so we need to clear the canvas so that\n", 123 " // there is no ghosting.\n", 124 " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", 125 " }\n", 126 " fig.context.drawImage(fig.imageObj, 0, 0);\n", 127 " };\n", 128 "\n", 129 " this.imageObj.onunload = function () {\n", 130 " fig.ws.close();\n", 131 " };\n", 132 "\n", 133 " this.ws.onmessage = this._make_on_message_function(this);\n", 134 "\n", 135 " this.ondownload = ondownload;\n", 136 "};\n", 137 "\n", 138 "mpl.figure.prototype._init_header = function () {\n", 139 " var titlebar = document.createElement('div');\n", 140 " titlebar.classList =\n", 141 " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", 142 " var titletext = document.createElement('div');\n", 143 " titletext.classList = 'ui-dialog-title';\n", 144 " titletext.setAttribute(\n", 145 " 'style',\n", 146 " 'width: 100%; text-align: center; padding: 3px;'\n", 147 " );\n", 148 " titlebar.appendChild(titletext);\n", 149 " this.root.appendChild(titlebar);\n", 150 " this.header = titletext;\n", 151 "};\n", 152 "\n", 153 "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", 154 "\n", 155 "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", 156 "\n", 157 "mpl.figure.prototype._init_canvas = function () {\n", 158 " var fig = this;\n", 159 "\n", 160 " var canvas_div = (this.canvas_div = document.createElement('div'));\n", 161 " canvas_div.setAttribute(\n", 162 " 'style',\n", 163 " 'border: 1px solid #ddd;' +\n", 164 " 'box-sizing: content-box;' +\n", 165 " 'clear: both;' +\n", 166 " 'min-height: 1px;' +\n", 167 " 'min-width: 1px;' +\n", 168 " 'outline: 0;' +\n", 169 " 'overflow: hidden;' +\n", 170 " 'position: relative;' +\n", 171 " 'resize: both;'\n", 172 " );\n", 173 "\n", 174 " function on_keyboard_event_closure(name) {\n", 175 " return function (event) {\n", 176 " return fig.key_event(event, name);\n", 177 " };\n", 178 " }\n", 179 "\n", 180 " canvas_div.addEventListener(\n", 181 " 'keydown',\n", 182 " on_keyboard_event_closure('key_press')\n", 183 " );\n", 184 " canvas_div.addEventListener(\n", 185 " 'keyup',\n", 186 " on_keyboard_event_closure('key_release')\n", 187 " );\n", 188 "\n", 189 " this._canvas_extra_style(canvas_div);\n", 190 " this.root.appendChild(canvas_div);\n", 191 "\n", 192 " var canvas = (this.canvas = document.createElement('canvas'));\n", 193 " canvas.classList.add('mpl-canvas');\n", 194 " canvas.setAttribute('style', 'box-sizing: content-box;');\n", 195 "\n", 196 " this.context = canvas.getContext('2d');\n", 197 "\n", 198 " var backingStore =\n", 199 " this.context.backingStorePixelRatio ||\n", 200 " this.context.webkitBackingStorePixelRatio ||\n", 201 " this.context.mozBackingStorePixelRatio ||\n", 202 " this.context.msBackingStorePixelRatio ||\n", 203 " this.context.oBackingStorePixelRatio ||\n", 204 " this.context.backingStorePixelRatio ||\n", 205 " 1;\n", 206 "\n", 207 " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", 208 "\n", 209 " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", 210 " 'canvas'\n", 211 " ));\n", 212 " rubberband_canvas.setAttribute(\n", 213 " 'style',\n", 214 " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", 215 " );\n", 216 "\n", 217 " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", 218 " if (this.ResizeObserver === undefined) {\n", 219 " if (window.ResizeObserver !== undefined) {\n", 220 " this.ResizeObserver = window.ResizeObserver;\n", 221 " } else {\n", 222 " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", 223 " this.ResizeObserver = obs.ResizeObserver;\n", 224 " }\n", 225 " }\n", 226 "\n", 227 " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", 228 " var nentries = entries.length;\n", 229 " for (var i = 0; i < nentries; i++) {\n", 230 " var entry = entries[i];\n", 231 " var width, height;\n", 232 " if (entry.contentBoxSize) {\n", 233 " if (entry.contentBoxSize instanceof Array) {\n", 234 " // Chrome 84 implements new version of spec.\n", 235 " width = entry.contentBoxSize[0].inlineSize;\n", 236 " height = entry.contentBoxSize[0].blockSize;\n", 237 " } else {\n", 238 " // Firefox implements old version of spec.\n", 239 " width = entry.contentBoxSize.inlineSize;\n", 240 " height = entry.contentBoxSize.blockSize;\n", 241 " }\n", 242 " } else {\n", 243 " // Chrome <84 implements even older version of spec.\n", 244 " width = entry.contentRect.width;\n", 245 " height = entry.contentRect.height;\n", 246 " }\n", 247 "\n", 248 " // Keep the size of the canvas and rubber band canvas in sync with\n", 249 " // the canvas container.\n", 250 " if (entry.devicePixelContentBoxSize) {\n", 251 " // Chrome 84 implements new version of spec.\n", 252 " canvas.setAttribute(\n", 253 " 'width',\n", 254 " entry.devicePixelContentBoxSize[0].inlineSize\n", 255 " );\n", 256 " canvas.setAttribute(\n", 257 " 'height',\n", 258 " entry.devicePixelContentBoxSize[0].blockSize\n", 259 " );\n", 260 " } else {\n", 261 " canvas.setAttribute('width', width * fig.ratio);\n", 262 " canvas.setAttribute('height', height * fig.ratio);\n", 263 " }\n", 264 " canvas.setAttribute(\n", 265 " 'style',\n", 266 " 'width: ' + width + 'px; height: ' + height + 'px;'\n", 267 " );\n", 268 "\n", 269 " rubberband_canvas.setAttribute('width', width);\n", 270 " rubberband_canvas.setAttribute('height', height);\n", 271 "\n", 272 " // And update the size in Python. We ignore the initial 0/0 size\n", 273 " // that occurs as the element is placed into the DOM, which should\n", 274 " // otherwise not happen due to the minimum size styling.\n", 275 " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", 276 " fig.request_resize(width, height);\n", 277 " }\n", 278 " }\n", 279 " });\n", 280 " this.resizeObserverInstance.observe(canvas_div);\n", 281 "\n", 282 " function on_mouse_event_closure(name) {\n", 283 " return function (event) {\n", 284 " return fig.mouse_event(event, name);\n", 285 " };\n", 286 " }\n", 287 "\n", 288 " rubberband_canvas.addEventListener(\n", 289 " 'mousedown',\n", 290 " on_mouse_event_closure('button_press')\n", 291 " );\n", 292 " rubberband_canvas.addEventListener(\n", 293 " 'mouseup',\n", 294 " on_mouse_event_closure('button_release')\n", 295 " );\n", 296 " // Throttle sequential mouse events to 1 every 20ms.\n", 297 " rubberband_canvas.addEventListener(\n", 298 " 'mousemove',\n", 299 " on_mouse_event_closure('motion_notify')\n", 300 " );\n", 301 "\n", 302 " rubberband_canvas.addEventListener(\n", 303 " 'mouseenter',\n", 304 " on_mouse_event_closure('figure_enter')\n", 305 " );\n", 306 " rubberband_canvas.addEventListener(\n", 307 " 'mouseleave',\n", 308 " on_mouse_event_closure('figure_leave')\n", 309 " );\n", 310 "\n", 311 " canvas_div.addEventListener('wheel', function (event) {\n", 312 " if (event.deltaY < 0) {\n", 313 " event.step = 1;\n", 314 " } else {\n", 315 " event.step = -1;\n", 316 " }\n", 317 " on_mouse_event_closure('scroll')(event);\n", 318 " });\n", 319 "\n", 320 " canvas_div.appendChild(canvas);\n", 321 " canvas_div.appendChild(rubberband_canvas);\n", 322 "\n", 323 " this.rubberband_context = rubberband_canvas.getContext('2d');\n", 324 " this.rubberband_context.strokeStyle = '#000000';\n", 325 "\n", 326 " this._resize_canvas = function (width, height, forward) {\n", 327 " if (forward) {\n", 328 " canvas_div.style.width = width + 'px';\n", 329 " canvas_div.style.height = height + 'px';\n", 330 " }\n", 331 " };\n", 332 "\n", 333 " // Disable right mouse context menu.\n", 334 " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", 335 " event.preventDefault();\n", 336 " return false;\n", 337 " });\n", 338 "\n", 339 " function set_focus() {\n", 340 " canvas.focus();\n", 341 " canvas_div.focus();\n", 342 " }\n", 343 "\n", 344 " window.setTimeout(set_focus, 100);\n", 345 "};\n", 346 "\n", 347 "mpl.figure.prototype._init_toolbar = function () {\n", 348 " var fig = this;\n", 349 "\n", 350 " var toolbar = document.createElement('div');\n", 351 " toolbar.classList = 'mpl-toolbar';\n", 352 " this.root.appendChild(toolbar);\n", 353 "\n", 354 " function on_click_closure(name) {\n", 355 " return function (_event) {\n", 356 " return fig.toolbar_button_onclick(name);\n", 357 " };\n", 358 " }\n", 359 "\n", 360 " function on_mouseover_closure(tooltip) {\n", 361 " return function (event) {\n", 362 " if (!event.currentTarget.disabled) {\n", 363 " return fig.toolbar_button_onmouseover(tooltip);\n", 364 " }\n", 365 " };\n", 366 " }\n", 367 "\n", 368 " fig.buttons = {};\n", 369 " var buttonGroup = document.createElement('div');\n", 370 " buttonGroup.classList = 'mpl-button-group';\n", 371 " for (var toolbar_ind in mpl.toolbar_items) {\n", 372 " var name = mpl.toolbar_items[toolbar_ind][0];\n", 373 " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", 374 " var image = mpl.toolbar_items[toolbar_ind][2];\n", 375 " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", 376 "\n", 377 " if (!name) {\n", 378 " /* Instead of a spacer, we start a new button group. */\n", 379 " if (buttonGroup.hasChildNodes()) {\n", 380 " toolbar.appendChild(buttonGroup);\n", 381 " }\n", 382 " buttonGroup = document.createElement('div');\n", 383 " buttonGroup.classList = 'mpl-button-group';\n", 384 " continue;\n", 385 " }\n", 386 "\n", 387 " var button = (fig.buttons[name] = document.createElement('button'));\n", 388 " button.classList = 'mpl-widget';\n", 389 " button.setAttribute('role', 'button');\n", 390 " button.setAttribute('aria-disabled', 'false');\n", 391 " button.addEventListener('click', on_click_closure(method_name));\n", 392 " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", 393 "\n", 394 " var icon_img = document.createElement('img');\n", 395 " icon_img.src = '_images/' + image + '.png';\n", 396 " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", 397 " icon_img.alt = tooltip;\n", 398 " button.appendChild(icon_img);\n", 399 "\n", 400 " buttonGroup.appendChild(button);\n", 401 " }\n", 402 "\n", 403 " if (buttonGroup.hasChildNodes()) {\n", 404 " toolbar.appendChild(buttonGroup);\n", 405 " }\n", 406 "\n", 407 " var fmt_picker = document.createElement('select');\n", 408 " fmt_picker.classList = 'mpl-widget';\n", 409 " toolbar.appendChild(fmt_picker);\n", 410 " this.format_dropdown = fmt_picker;\n", 411 "\n", 412 " for (var ind in mpl.extensions) {\n", 413 " var fmt = mpl.extensions[ind];\n", 414 " var option = document.createElement('option');\n", 415 " option.selected = fmt === mpl.default_extension;\n", 416 " option.innerHTML = fmt;\n", 417 " fmt_picker.appendChild(option);\n", 418 " }\n", 419 "\n", 420 " var status_bar = document.createElement('span');\n", 421 " status_bar.classList = 'mpl-message';\n", 422 " toolbar.appendChild(status_bar);\n", 423 " this.message = status_bar;\n", 424 "};\n", 425 "\n", 426 "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", 427 " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", 428 " // which will in turn request a refresh of the image.\n", 429 " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", 430 "};\n", 431 "\n", 432 "mpl.figure.prototype.send_message = function (type, properties) {\n", 433 " properties['type'] = type;\n", 434 " properties['figure_id'] = this.id;\n", 435 " this.ws.send(JSON.stringify(properties));\n", 436 "};\n", 437 "\n", 438 "mpl.figure.prototype.send_draw_message = function () {\n", 439 " if (!this.waiting) {\n", 440 " this.waiting = true;\n", 441 " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", 442 " }\n", 443 "};\n", 444 "\n", 445 "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", 446 " var format_dropdown = fig.format_dropdown;\n", 447 " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", 448 " fig.ondownload(fig, format);\n", 449 "};\n", 450 "\n", 451 "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", 452 " var size = msg['size'];\n", 453 " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", 454 " fig._resize_canvas(size[0], size[1], msg['forward']);\n", 455 " fig.send_message('refresh', {});\n", 456 " }\n", 457 "};\n", 458 "\n", 459 "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", 460 " var x0 = msg['x0'] / fig.ratio;\n", 461 " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", 462 " var x1 = msg['x1'] / fig.ratio;\n", 463 " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", 464 " x0 = Math.floor(x0) + 0.5;\n", 465 " y0 = Math.floor(y0) + 0.5;\n", 466 " x1 = Math.floor(x1) + 0.5;\n", 467 " y1 = Math.floor(y1) + 0.5;\n", 468 " var min_x = Math.min(x0, x1);\n", 469 " var min_y = Math.min(y0, y1);\n", 470 " var width = Math.abs(x1 - x0);\n", 471 " var height = Math.abs(y1 - y0);\n", 472 "\n", 473 " fig.rubberband_context.clearRect(\n", 474 " 0,\n", 475 " 0,\n", 476 " fig.canvas.width / fig.ratio,\n", 477 " fig.canvas.height / fig.ratio\n", 478 " );\n", 479 "\n", 480 " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", 481 "};\n", 482 "\n", 483 "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", 484 " // Updates the figure title.\n", 485 " fig.header.textContent = msg['label'];\n", 486 "};\n", 487 "\n", 488 "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", 489 " var cursor = msg['cursor'];\n", 490 " switch (cursor) {\n", 491 " case 0:\n", 492 " cursor = 'pointer';\n", 493 " break;\n", 494 " case 1:\n", 495 " cursor = 'default';\n", 496 " break;\n", 497 " case 2:\n", 498 " cursor = 'crosshair';\n", 499 " break;\n", 500 " case 3:\n", 501 " cursor = 'move';\n", 502 " break;\n", 503 " }\n", 504 " fig.rubberband_canvas.style.cursor = cursor;\n", 505 "};\n", 506 "\n", 507 "mpl.figure.prototype.handle_message = function (fig, msg) {\n", 508 " fig.message.textContent = msg['message'];\n", 509 "};\n", 510 "\n", 511 "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", 512 " // Request the server to send over a new figure.\n", 513 " fig.send_draw_message();\n", 514 "};\n", 515 "\n", 516 "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", 517 " fig.image_mode = msg['mode'];\n", 518 "};\n", 519 "\n", 520 "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", 521 " for (var key in msg) {\n", 522 " if (!(key in fig.buttons)) {\n", 523 " continue;\n", 524 " }\n", 525 " fig.buttons[key].disabled = !msg[key];\n", 526 " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", 527 " }\n", 528 "};\n", 529 "\n", 530 "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", 531 " if (msg['mode'] === 'PAN') {\n", 532 " fig.buttons['Pan'].classList.add('active');\n", 533 " fig.buttons['Zoom'].classList.remove('active');\n", 534 " } else if (msg['mode'] === 'ZOOM') {\n", 535 " fig.buttons['Pan'].classList.remove('active');\n", 536 " fig.buttons['Zoom'].classList.add('active');\n", 537 " } else {\n", 538 " fig.buttons['Pan'].classList.remove('active');\n", 539 " fig.buttons['Zoom'].classList.remove('active');\n", 540 " }\n", 541 "};\n", 542 "\n", 543 "mpl.figure.prototype.updated_canvas_event = function () {\n", 544 " // Called whenever the canvas gets updated.\n", 545 " this.send_message('ack', {});\n", 546 "};\n", 547 "\n", 548 "// A function to construct a web socket function for onmessage handling.\n", 549 "// Called in the figure constructor.\n", 550 "mpl.figure.prototype._make_on_message_function = function (fig) {\n", 551 " return function socket_on_message(evt) {\n", 552 " if (evt.data instanceof Blob) {\n", 553 " /* FIXME: We get \"Resource interpreted as Image but\n", 554 " * transferred with MIME type text/plain:\" errors on\n", 555 " * Chrome. But how to set the MIME type? It doesn't seem\n", 556 " * to be part of the websocket stream */\n", 557 " evt.data.type = 'image/png';\n", 558 "\n", 559 " /* Free the memory for the previous frames */\n", 560 " if (fig.imageObj.src) {\n", 561 " (window.URL || window.webkitURL).revokeObjectURL(\n", 562 " fig.imageObj.src\n", 563 " );\n", 564 " }\n", 565 "\n", 566 " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", 567 " evt.data\n", 568 " );\n", 569 " fig.updated_canvas_event();\n", 570 " fig.waiting = false;\n", 571 " return;\n", 572 " } else if (\n", 573 " typeof evt.data === 'string' &&\n", 574 " evt.data.slice(0, 21) === 'data:image/png;base64'\n", 575 " ) {\n", 576 " fig.imageObj.src = evt.data;\n", 577 " fig.updated_canvas_event();\n", 578 " fig.waiting = false;\n", 579 " return;\n", 580 " }\n", 581 "\n", 582 " var msg = JSON.parse(evt.data);\n", 583 " var msg_type = msg['type'];\n", 584 "\n", 585 " // Call the \"handle_{type}\" callback, which takes\n", 586 " // the figure and JSON message as its only arguments.\n", 587 " try {\n", 588 " var callback = fig['handle_' + msg_type];\n", 589 " } catch (e) {\n", 590 " console.log(\n", 591 " \"No handler for the '\" + msg_type + \"' message type: \",\n", 592 " msg\n", 593 " );\n", 594 " return;\n", 595 " }\n", 596 "\n", 597 " if (callback) {\n", 598 " try {\n", 599 " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", 600 " callback(fig, msg);\n", 601 " } catch (e) {\n", 602 " console.log(\n", 603 " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", 604 " e,\n", 605 " e.stack,\n", 606 " msg\n", 607 " );\n", 608 " }\n", 609 " }\n", 610 " };\n", 611 "};\n", 612 "\n", 613 "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", 614 "mpl.findpos = function (e) {\n", 615 " //this section is from http://www.quirksmode.org/js/events_properties.html\n", 616 " var targ;\n", 617 " if (!e) {\n", 618 " e = window.event;\n", 619 " }\n", 620 " if (e.target) {\n", 621 " targ = e.target;\n", 622 " } else if (e.srcElement) {\n", 623 " targ = e.srcElement;\n", 624 " }\n", 625 " if (targ.nodeType === 3) {\n", 626 " // defeat Safari bug\n", 627 " targ = targ.parentNode;\n", 628 " }\n", 629 "\n", 630 " // pageX,Y are the mouse positions relative to the document\n", 631 " var boundingRect = targ.getBoundingClientRect();\n", 632 " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", 633 " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", 634 "\n", 635 " return { x: x, y: y };\n", 636 "};\n", 637 "\n", 638 "/*\n", 639 " * return a copy of an object with only non-object keys\n", 640 " * we need this to avoid circular references\n", 641 " * http://stackoverflow.com/a/24161582/3208463\n", 642 " */\n", 643 "function simpleKeys(original) {\n", 644 " return Object.keys(original).reduce(function (obj, key) {\n", 645 " if (typeof original[key] !== 'object') {\n", 646 " obj[key] = original[key];\n", 647 " }\n", 648 " return obj;\n", 649 " }, {});\n", 650 "}\n", 651 "\n", 652 "mpl.figure.prototype.mouse_event = function (event, name) {\n", 653 " var canvas_pos = mpl.findpos(event);\n", 654 "\n", 655 " if (name === 'button_press') {\n", 656 " this.canvas.focus();\n", 657 " this.canvas_div.focus();\n", 658 " }\n", 659 "\n", 660 " var x = canvas_pos.x * this.ratio;\n", 661 " var y = canvas_pos.y * this.ratio;\n", 662 "\n", 663 " this.send_message(name, {\n", 664 " x: x,\n", 665 " y: y,\n", 666 " button: event.button,\n", 667 " step: event.step,\n", 668 " guiEvent: simpleKeys(event),\n", 669 " });\n", 670 "\n", 671 " /* This prevents the web browser from automatically changing to\n", 672 " * the text insertion cursor when the button is pressed. We want\n", 673 " * to control all of the cursor setting manually through the\n", 674 " * 'cursor' event from matplotlib */\n", 675 " event.preventDefault();\n", 676 " return false;\n", 677 "};\n", 678 "\n", 679 "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", 680 " // Handle any extra behaviour associated with a key event\n", 681 "};\n", 682 "\n", 683 "mpl.figure.prototype.key_event = function (event, name) {\n", 684 " // Prevent repeat events\n", 685 " if (name === 'key_press') {\n", 686 " if (event.which === this._key) {\n", 687 " return;\n", 688 " } else {\n", 689 " this._key = event.which;\n", 690 " }\n", 691 " }\n", 692 " if (name === 'key_release') {\n", 693 " this._key = null;\n", 694 " }\n", 695 "\n", 696 " var value = '';\n", 697 " if (event.ctrlKey && event.which !== 17) {\n", 698 " value += 'ctrl+';\n", 699 " }\n", 700 " if (event.altKey && event.which !== 18) {\n", 701 " value += 'alt+';\n", 702 " }\n", 703 " if (event.shiftKey && event.which !== 16) {\n", 704 " value += 'shift+';\n", 705 " }\n", 706 "\n", 707 " value += 'k';\n", 708 " value += event.which.toString();\n", 709 "\n", 710 " this._key_event_extra(event, name);\n", 711 "\n", 712 " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", 713 " return false;\n", 714 "};\n", 715 "\n", 716 "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", 717 " if (name === 'download') {\n", 718 " this.handle_save(this, null);\n", 719 " } else {\n", 720 " this.send_message('toolbar_button', { name: name });\n", 721 " }\n", 722 "};\n", 723 "\n", 724 "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", 725 " this.message.textContent = tooltip;\n", 726 "};\n", 727 "\n", 728 "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", 729 "// prettier-ignore\n", 730 "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", 731 "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"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", 732 "\n", 733 "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", 734 "\n", 735 "mpl.default_extension = \"png\";/* global mpl */\n", 736 "\n", 737 "var comm_websocket_adapter = function (comm) {\n", 738 " // Create a \"websocket\"-like object which calls the given IPython comm\n", 739 " // object with the appropriate methods. Currently this is a non binary\n", 740 " // socket, so there is still some room for performance tuning.\n", 741 " var ws = {};\n", 742 "\n", 743 " ws.close = function () {\n", 744 " comm.close();\n", 745 " };\n", 746 " ws.send = function (m) {\n", 747 " //console.log('sending', m);\n", 748 " comm.send(m);\n", 749 " };\n", 750 " // Register the callback with on_msg.\n", 751 " comm.on_msg(function (msg) {\n", 752 " //console.log('receiving', msg['content']['data'], msg);\n", 753 " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", 754 " ws.onmessage(msg['content']['data']);\n", 755 " });\n", 756 " return ws;\n", 757 "};\n", 758 "\n", 759 "mpl.mpl_figure_comm = function (comm, msg) {\n", 760 " // This is the function which gets called when the mpl process\n", 761 " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", 762 "\n", 763 " var id = msg.content.data.id;\n", 764 " // Get hold of the div created by the display call when the Comm\n", 765 " // socket was opened in Python.\n", 766 " var element = document.getElementById(id);\n", 767 " var ws_proxy = comm_websocket_adapter(comm);\n", 768 "\n", 769 " function ondownload(figure, _format) {\n", 770 " window.open(figure.canvas.toDataURL());\n", 771 " }\n", 772 "\n", 773 " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", 774 "\n", 775 " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", 776 " // web socket which is closed, not our websocket->open comm proxy.\n", 777 " ws_proxy.onopen();\n", 778 "\n", 779 " fig.parent_element = element;\n", 780 " fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", 781 " if (!fig.cell_info) {\n", 782 " console.error('Failed to find cell for figure', id, fig);\n", 783 " return;\n", 784 " }\n", 785 " fig.cell_info[0].output_area.element.on(\n", 786 " 'cleared',\n", 787 " { fig: fig },\n", 788 " fig._remove_fig_handler\n", 789 " );\n", 790 "};\n", 791 "\n", 792 "mpl.figure.prototype.handle_close = function (fig, msg) {\n", 793 " var width = fig.canvas.width / fig.ratio;\n", 794 " fig.cell_info[0].output_area.element.off(\n", 795 " 'cleared',\n", 796 " fig._remove_fig_handler\n", 797 " );\n", 798 " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", 799 "\n", 800 " // Update the output cell to use the data from the current canvas.\n", 801 " fig.push_to_output();\n", 802 " var dataURL = fig.canvas.toDataURL();\n", 803 " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", 804 " // the notebook keyboard shortcuts fail.\n", 805 " IPython.keyboard_manager.enable();\n", 806 " fig.parent_element.innerHTML =\n", 807 " '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", 808 " fig.close_ws(fig, msg);\n", 809 "};\n", 810 "\n", 811 "mpl.figure.prototype.close_ws = function (fig, msg) {\n", 812 " fig.send_message('closing', msg);\n", 813 " // fig.ws.close()\n", 814 "};\n", 815 "\n", 816 "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", 817 " // Turn the data on the canvas into data in the output cell.\n", 818 " var width = this.canvas.width / this.ratio;\n", 819 " var dataURL = this.canvas.toDataURL();\n", 820 " this.cell_info[1]['text/html'] =\n", 821 " '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", 822 "};\n", 823 "\n", 824 "mpl.figure.prototype.updated_canvas_event = function () {\n", 825 " // Tell IPython that the notebook contents must change.\n", 826 " IPython.notebook.set_dirty(true);\n", 827 " this.send_message('ack', {});\n", 828 " var fig = this;\n", 829 " // Wait a second, then push the new image to the DOM so\n", 830 " // that it is saved nicely (might be nice to debounce this).\n", 831 " setTimeout(function () {\n", 832 " fig.push_to_output();\n", 833 " }, 1000);\n", 834 "};\n", 835 "\n", 836 "mpl.figure.prototype._init_toolbar = function () {\n", 837 " var fig = this;\n", 838 "\n", 839 " var toolbar = document.createElement('div');\n", 840 " toolbar.classList = 'btn-toolbar';\n", 841 " this.root.appendChild(toolbar);\n", 842 "\n", 843 " function on_click_closure(name) {\n", 844 " return function (_event) {\n", 845 " return fig.toolbar_button_onclick(name);\n", 846 " };\n", 847 " }\n", 848 "\n", 849 " function on_mouseover_closure(tooltip) {\n", 850 " return function (event) {\n", 851 " if (!event.currentTarget.disabled) {\n", 852 " return fig.toolbar_button_onmouseover(tooltip);\n", 853 " }\n", 854 " };\n", 855 " }\n", 856 "\n", 857 " fig.buttons = {};\n", 858 " var buttonGroup = document.createElement('div');\n", 859 " buttonGroup.classList = 'btn-group';\n", 860 " var button;\n", 861 " for (var toolbar_ind in mpl.toolbar_items) {\n", 862 " var name = mpl.toolbar_items[toolbar_ind][0];\n", 863 " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", 864 " var image = mpl.toolbar_items[toolbar_ind][2];\n", 865 " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", 866 "\n", 867 " if (!name) {\n", 868 " /* Instead of a spacer, we start a new button group. */\n", 869 " if (buttonGroup.hasChildNodes()) {\n", 870 " toolbar.appendChild(buttonGroup);\n", 871 " }\n", 872 " buttonGroup = document.createElement('div');\n", 873 " buttonGroup.classList = 'btn-group';\n", 874 " continue;\n", 875 " }\n", 876 "\n", 877 " button = fig.buttons[name] = document.createElement('button');\n", 878 " button.classList = 'btn btn-default';\n", 879 " button.href = '#';\n", 880 " button.title = name;\n", 881 " button.innerHTML = '<i class=\"fa ' + image + ' fa-lg\"></i>';\n", 882 " button.addEventListener('click', on_click_closure(method_name));\n", 883 " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", 884 " buttonGroup.appendChild(button);\n", 885 " }\n", 886 "\n", 887 " if (buttonGroup.hasChildNodes()) {\n", 888 " toolbar.appendChild(buttonGroup);\n", 889 " }\n", 890 "\n", 891 " // Add the status bar.\n", 892 " var status_bar = document.createElement('span');\n", 893 " status_bar.classList = 'mpl-message pull-right';\n", 894 " toolbar.appendChild(status_bar);\n", 895 " this.message = status_bar;\n", 896 "\n", 897 " // Add the close button to the window.\n", 898 " var buttongrp = document.createElement('div');\n", 899 " buttongrp.classList = 'btn-group inline pull-right';\n", 900 " button = document.createElement('button');\n", 901 " button.classList = 'btn btn-mini btn-primary';\n", 902 " button.href = '#';\n", 903 " button.title = 'Stop Interaction';\n", 904 " button.innerHTML = '<i class=\"fa fa-power-off icon-remove icon-large\"></i>';\n", 905 " button.addEventListener('click', function (_evt) {\n", 906 " fig.handle_close(fig, {});\n", 907 " });\n", 908 " button.addEventListener(\n", 909 " 'mouseover',\n", 910 " on_mouseover_closure('Stop Interaction')\n", 911 " );\n", 912 " buttongrp.appendChild(button);\n", 913 " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", 914 " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", 915 "};\n", 916 "\n", 917 "mpl.figure.prototype._remove_fig_handler = function (event) {\n", 918 " var fig = event.data.fig;\n", 919 " if (event.target !== this) {\n", 920 " // Ignore bubbled events from children.\n", 921 " return;\n", 922 " }\n", 923 " fig.close_ws(fig, {});\n", 924 "};\n", 925 "\n", 926 "mpl.figure.prototype._root_extra_style = function (el) {\n", 927 " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", 928 "};\n", 929 "\n", 930 "mpl.figure.prototype._canvas_extra_style = function (el) {\n", 931 " // this is important to make the div 'focusable\n", 932 " el.setAttribute('tabindex', 0);\n", 933 " // reach out to IPython and tell the keyboard manager to turn it's self\n", 934 " // off when our div gets focus\n", 935 "\n", 936 " // location in version 3\n", 937 " if (IPython.notebook.keyboard_manager) {\n", 938 " IPython.notebook.keyboard_manager.register_events(el);\n", 939 " } else {\n", 940 " // location in version 2\n", 941 " IPython.keyboard_manager.register_events(el);\n", 942 " }\n", 943 "};\n", 944 "\n", 945 "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", 946 " var manager = IPython.notebook.keyboard_manager;\n", 947 " if (!manager) {\n", 948 " manager = IPython.keyboard_manager;\n", 949 " }\n", 950 "\n", 951 " // Check for shift+enter\n", 952 " if (event.shiftKey && event.which === 13) {\n", 953 " this.canvas_div.blur();\n", 954 " // select the cell after this one\n", 955 " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", 956 " IPython.notebook.select(index + 1);\n", 957 " }\n", 958 "};\n", 959 "\n", 960 "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", 961 " fig.ondownload(fig, null);\n", 962 "};\n", 963 "\n", 964 "mpl.find_output_cell = function (html_output) {\n", 965 " // Return the cell and output element which can be found *uniquely* in the notebook.\n", 966 " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", 967 " // IPython event is triggered only after the cells have been serialised, which for\n", 968 " // our purposes (turning an active figure into a static one), is too late.\n", 969 " var cells = IPython.notebook.get_cells();\n", 970 " var ncells = cells.length;\n", 971 " for (var i = 0; i < ncells; i++) {\n", 972 " var cell = cells[i];\n", 973 " if (cell.cell_type === 'code') {\n", 974 " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", 975 " var data = cell.output_area.outputs[j];\n", 976 " if (data.data) {\n", 977 " // IPython >= 3 moved mimebundle to data attribute of output\n", 978 " data = data.data;\n", 979 " }\n", 980 " if (data['text/html'] === html_output) {\n", 981 " return [cell, data, j];\n", 982 " }\n", 983 " }\n", 984 " }\n", 985 " }\n", 986 "};\n", 987 "\n", 988 "// Register the function which deals with the matplotlib target/channel.\n", 989 "// The kernel may be null if the page has been refreshed.\n", 990 "if (IPython.notebook.kernel !== null) {\n", 991 " IPython.notebook.kernel.comm_manager.register_target(\n", 992 " 'matplotlib',\n", 993 " mpl.mpl_figure_comm\n", 994 " );\n", 995 "}\n" 996 ], 997 "text/plain": [ 998 "<IPython.core.display.Javascript object>" 999 ] 1000 }, 1001 "metadata": {}, 1002 "output_type": "display_data" 1003 }, 1004 { 1005 "data": { 1006 "text/html": [ 1007 "<img src=\"data:image/png;base64,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\" width=\"450\">" 1008 ], 1009 "text/plain": [ 1010 "<IPython.core.display.HTML object>" 1011 ] 1012 }, 1013 "metadata": {}, 1014 "output_type": "display_data" 1015 } 1016 ], 1017 "source": [ 1018 "plt.figure(figsize=(4.5,3))\n", 1019 "plt.semilogy(err_apophis[:,0], err_apophis[:,1], 'C7.', label=\"Position error (m)\")\n", 1020 "plt.semilogy(err_apophis[:,0], err_apophis[:,2], 'k.', label=\"Velocity error (m/s)\")\n", 1021 "plt.xlabel(\"Time to close approach (years)\")\n", 1022 "plt.ylabel(\"Error (see legend)\")\n", 1023 "plt.legend()\n", 1024 "plt.tight_layout()" 1025 ] 1026 }, 1027 { 1028 "cell_type": "code", 1029 "execution_count": 28, 1030 "metadata": {}, 1031 "outputs": [], 1032 "source": [ 1033 "plt.savefig(\"apophis.png\", dpi = 600)" 1034 ] 1035 }, 1036 { 1037 "cell_type": "code", 1038 "execution_count": null, 1039 "metadata": {}, 1040 "outputs": [], 1041 "source": [] 1042 }, 1043 { 1044 "cell_type": "code", 1045 "execution_count": null, 1046 "metadata": {}, 1047 "outputs": [], 1048 "source": [] 1049 } 1050 ], 1051 "metadata": { 1052 "kernelspec": { 1053 "display_name": "Python 3", 1054 "language": "python", 1055 "name": "python3" 1056 }, 1057 "language_info": { 1058 "codemirror_mode": { 1059 "name": "ipython", 1060 "version": 3 1061 }, 1062 "file_extension": ".py", 1063 "mimetype": "text/x-python", 1064 "name": "python", 1065 "nbconvert_exporter": "python", 1066 "pygments_lexer": "ipython3", 1067 "version": "3.8.6" 1068 } 1069 }, 1070 "nbformat": 4, 1071 "nbformat_minor": 4 1072} 1073