{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Custom Display Logic Exercises"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from IPython.display import display\n",
    "from IPython.display import (\n",
    "    display_png, display_html, display_latex,\n",
    "    display_javascript, display_svg\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Circle class with custom display methods"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Write a simple `MyCircle` Python class. Here is a skeleton to get you started:\n",
    "\n",
    "```python\n",
    "class MyCircle(object):\n",
    "    def __init__(self, center=(0.0,0.0), radius=1.0, color='blue'):\n",
    "        self.center = center\n",
    "        self.radius = radius\n",
    "        self.color = color\n",
    "```\n",
    "\n",
    "Now add special display methods to this class for the following representations (remember to wrap them in Python strings):\n",
    "\n",
    "For HTML:\n",
    "\n",
    "    &#x25CB;\n",
    "\n",
    "For SVG:\n",
    "\n",
    "    <svg width=\"100px\" height=\"100px\">\n",
    "        <circle cx=\"50\" cy=\"50\" r=\"20\" stroke=\"black\" stroke-width=\"1\" fill=\"white\"/>\n",
    "    </svg>\n",
    "\n",
    "For LaTeX (wrap with `$` and use a raw Python string):\n",
    "\n",
    "    \\bigcirc\n",
    "\n",
    "For JavaScript:\n",
    "\n",
    "    alert('I am a circle!');\n",
    "\n",
    "After you write the class, create an instance and then use `display_html`, `display_svg`, `display_latex` and `display_javascript` to display those representations."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Solution"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here is the solution to the simple `MyCircle` class:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%load soln/mycircle.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now create an instance and use the display methods:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "c = MyCircle()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "display(c)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "display_html(c)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "display_svg(c)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "display_latex(c)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "display_javascript(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## PNG formatter for `MyCircle`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "from matplotlib import pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's assume that the `MyCircle` class has already been defined and add a PNG representation using a formatter display function. Here is a function that converts a `MyCircle` instance to raw PNG data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from IPython.core.pylabtools import print_figure\n",
    "\n",
    "def circle_to_png(circle):\n",
    "    \"\"\"Render AnotherCircle to png data using matplotlib\"\"\"\n",
    "    fig, ax = plt.subplots()\n",
    "    patch = plt.Circle(circle.center,\n",
    "                       radius=circle.radius,\n",
    "                       fc=circle.color,\n",
    "                       )\n",
    "    ax.add_patch(patch)\n",
    "    plt.axis('scaled')\n",
    "    data = print_figure(fig, 'png')\n",
    "    # We MUST close the figure, otherwise IPython's display machinery\n",
    "    # will pick it up and send it as output, resulting in a double display\n",
    "    plt.close(fig)\n",
    "    return data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now use the IPython API to get the PNG formatter (`image/png`) and call the `for_type` method to register `circle_to_png` as the display function for `MyCircle`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%load soln/mycircle_png.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "display_png(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## PNG formatter for NumPy arrays"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this exercise, you will register a display formatter function that generates a PNG representation of a 2d NumPy array. Here is the function that uses the [Python Imaging Library (PIL)](http://www.pythonware.com/products/pil/) to generate the raw PNG data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from PIL import Image\n",
    "from io import BytesIO\n",
    "import numpy as np\n",
    "\n",
    "def ndarray_to_png(x):\n",
    "    if len(x.shape) != 2: return\n",
    "    x = np.asarray(Image.fromarray(x).resize((500, 500)))\n",
    "    x = (x - x.min()) / (x.max() - x.min())\n",
    "    img = Image.fromarray((x*256).astype('uint8'))\n",
    "    img_buffer = BytesIO()\n",
    "    img.save(img_buffer, format='png')\n",
    "    return img_buffer.getvalue()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Use the `for_type` method of the PNG formatter to register `ndarray_to_png` as the display function for `np.ndarray`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%load soln/ndarray_png.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now create a few NumPy arrays and display them. Notice that their default representation in the Notebook is PNG rather than text."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "a = np.random.rand(100,100)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can still display the plain text representation using the `display_pretty` function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from IPython.display import display_pretty"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "display_pretty(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "b = np.linspace(0,100.0, 100**2).reshape((100,100))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "b"
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 0
}