{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import panel as pn\n",
    "\n",
    "pn.extension('ipywidgets')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ``Matplotlib`` pane allows displaying any displayable [Matplotlib](http://matplotlib.org) figure inside a Panel app. It will render the plot to PNG at the declared DPI and then embed it. If you find the figure to be clipped on the edges, you can set `tight=True` to automatically resize objects to fit within the pane.\n",
    "\n",
    "#### Parameters:\n",
    "\n",
    "* **``dpi``** (int, default=144): The dots per inch of the exported png\n",
    "* **``format``** (str, default='png'): The format to render the plot to.\n",
    "* **``interactive``** (boolean, default=False): Whether to use the interactive ipympl backend\n",
    "* **``tight``** (bool, default=False): Automatically adjust the figure size to fit the subplots and other artist elements.\n",
    "* **``object``** (matplotlib.Figure): The Matplotlib Figure object to display\n",
    "\n",
    "___"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that the examples below do not make use of the common ``matplotlib.pyplot`` API in order to avoid having to close the figure. If not closed, it could cause memory leaks and cause the `inline` backend to automatically display the figure. It is actually as simple as creating a `matplotlib.Figure` object, registering axes to this figure, and passing the figure to the `Matplotlib` pane that will take care of rendering it.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "from matplotlib.figure import Figure\n",
    "from matplotlib import cm\n",
    "from matplotlib.backends.backend_agg import FigureCanvas  # not needed for mpl >= 3.1\n",
    "\n",
    "Y, X = np.mgrid[-3:3:100j, -3:3:100j]\n",
    "U = -1 - X**2 + Y\n",
    "V = 1 + X - Y**2\n",
    "speed = np.sqrt(U*U + V*V)\n",
    "\n",
    "fig0 = Figure(figsize=(8, 6))\n",
    "ax0 = fig0.subplots()\n",
    "FigureCanvas(fig0)  # not needed for mpl >= 3.1\n",
    "\n",
    "strm = ax0.streamplot(X, Y, U, V, color=U, linewidth=2, cmap=cm.autumn)\n",
    "fig0.colorbar(strm.lines)\n",
    "\n",
    "mpl_pane = pn.pane.Matplotlib(fig0, dpi=144)\n",
    "mpl_pane"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By modifying the figure and using the ``trigger`` method on the pane's object we can easily update the plot:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "strm.lines.set_cmap(cm.viridis)\n",
    "\n",
    "mpl_pane.param.trigger('object')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Alternatively, like all other models, a ``Matplotlib`` pane can be updated by setting the ``object`` directly:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from mpl_toolkits.mplot3d import axes3d\n",
    "\n",
    "fig3d = Figure(figsize=(8, 6))\n",
    "ax = fig3d.add_subplot(111, projection='3d')\n",
    "FigureCanvas(fig3d)  # not needed for mpl >= 3.1\n",
    "\n",
    "X, Y, Z = axes3d.get_test_data(0.05)\n",
    "ax.plot_surface(X, Y, Z, rstride=8, cstride=8, alpha=0.3)\n",
    "cset = ax.contourf(X, Y, Z, zdir='z', offset=-100, cmap=cm.coolwarm)\n",
    "cset = ax.contourf(X, Y, Z, zdir='x', offset=-40, cmap=cm.coolwarm)\n",
    "cset = ax.contourf(X, Y, Z, zdir='y', offset=40, cmap=cm.coolwarm)\n",
    "\n",
    "ax.set_xlabel('X')\n",
    "ax.set_xlim(-40, 40)\n",
    "ax.set_ylabel('Y')\n",
    "ax.set_ylim(-40, 40)\n",
    "ax.set_zlabel('Z')\n",
    "ax.set_zlim(-100, 100)\n",
    "\n",
    "mpl_pane.object = fig3d"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Rendering\n",
    "\n",
    "The `Matplotlib` pane can render plots as `'png'` or `'svg'` by specifying the format. SVG output is an excellent choice if you want to rescale the plots arbitrarily:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.pane.Matplotlib(fig0, format='svg', sizing_mode='scale_both')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you have installed `ipympl` you will also be able to use the `interactive` backend:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = Figure(figsize=(8, 6))\n",
    "ax = fig.add_subplot(111)\n",
    "\n",
    "dx, dy = 0.05, 0.05\n",
    "\n",
    "# generate 2 2d grids for the x & y bounds\n",
    "y, x = np.mgrid[slice(1, 5 + dy, dy),\n",
    "                slice(1, 5 + dx, dx)]\n",
    "\n",
    "z = np.sin(x)**10 + np.cos(10 + y*x) * np.cos(x)\n",
    "\n",
    "cf = ax.contourf(x + dx/2., y + dy/2., z)\n",
    "fig.colorbar(cf, ax=ax)\n",
    "\n",
    "pn.pane.Matplotlib(fig, interactive=True, dpi=72)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Controls\n",
    "\n",
    "The `Matplotlib` pane exposes a number of options which can be changed from both Python and Javascript. Try out the effect of these parameters interactively:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn.Row(mpl_pane.controls(jslink=True), mpl_pane)"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
