{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Animation\n",
    "All (or most of) the changes in scatter and quiver plots are (linearly) interpolated. On top top that, scatter plots and quiver plots can take a sequence of arrays (the first dimension), where only one array is visualized. Together this can make smooth animations with coarse timesteps. Lets see an example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import ipyvolume.pylab as p3\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Basic animation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "34f2a8fe1e364a5c88238f8527b22498",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "A Jupyter Widget"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# only x is a sequence of arrays\n",
    "x = np.array([[-1, -0.8], [1, -0.1], [0., 0.5]])\n",
    "y = np.array([0.0, 0.0])\n",
    "z = np.array([0.0, 0.0])\n",
    "p3.figure()\n",
    "s = p3.scatter(x, y, z)\n",
    "p3.xyzlim(-1, 1)\n",
    "p3.animation_control(s) # shows controls for animation controls\n",
    "p3.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can control which array to visualize, using the `scatter.sequence_index` property. Actually, the `pylab.animate_glyphs` is connecting the `Slider` and `Play` button to that property, but you can also set it from Python."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "s.sequence_index = 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Animating color and size\n",
    "We now demonstrate that you can also animate color and size"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x,y and z are of shape (25, 25)\n",
      "and flattened of shape (625,)\n"
     ]
    }
   ],
   "source": [
    "# create 2d grids: x, y, and r \n",
    "u = np.linspace(-10, 10, 25)\n",
    "x, y = np.meshgrid(u, u)\n",
    "r = np.sqrt(x**2+y**2)\n",
    "print(\"x,y and z are of shape\", x.shape)\n",
    "# and turn them into 1d\n",
    "x = x.flatten()\n",
    "y = y.flatten()\n",
    "r = r.flatten()\n",
    "print(\"and flattened of shape\", x.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we only animate the z component"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "z is of shape (15, 625)\n"
     ]
    }
   ],
   "source": [
    "# create a sequence of 15 time elements\n",
    "time = np.linspace(0, np.pi*2, 15)\n",
    "z = np.array([(np.cos(r + t) * np.exp(-r/5)) for t in time])\n",
    "print(\"z is of shape\", z.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "12377583825640f79d948b1018cb56d3",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "A Jupyter Widget"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# draw the scatter plot, and add controls with animate_glyphs\n",
    "p3.figure()\n",
    "s = p3.scatter(x, z, y, marker=\"sphere\")\n",
    "p3.animation_control(s, interval=200)\n",
    "p3.ylim(-3,3)\n",
    "p3.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "color is of shape (15, 3, 625)\n"
     ]
    }
   ],
   "source": [
    "# Now also include, color, which containts rgb values\n",
    "color = np.array([[np.cos(r + t), 1-np.abs(z[i]), 0.1+z[i]*0] for i, t in enumerate(time)])\n",
    "size = (z+1)\n",
    "print(\"color is of shape\", color.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "color is of the wrong shape, the last dimension should contain the rgb value, i.e. the shape of should be (15, 2500, 3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "color = np.transpose(color, (0, 2, 1)) # flip the last axes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "27718ccf220f4597acda6c4e85e2f3ae",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "A Jupyter Widget"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "p3.figure()\n",
    "s = p3.scatter(x, z, y, color=color, size=size, marker=\"sphere\")\n",
    "p3.animation_control(s, interval=200)\n",
    "p3.ylim(-3,3)\n",
    "p3.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating movie files\n",
    "We now make a movie, with a 2 second duration, where we rotate the camera, and change the size of the scatter points. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "cc11e59f57114097944264265266769b",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "A Jupyter Widget"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def set_view(figure, framenr, fraction):\n",
    "    p3.view(fraction*360, (fraction - 0.5) * 180)\n",
    "    s.size = size * (1+0.5*np.sin(fraction * 6 * np.pi))\n",
    "p3.movie('wave.gif', set_view, fps=20, frames=40)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=\"data:image/gif;base64,[encoded data, 1595180 characters]\"/>"
      ],
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# include the gif with base64 encoding\n",
    "import IPython.display\n",
    "import base64\n",
    "with open('wave.gif', 'rb') as gif:\n",
    "    url = b\"data:image/gif;base64,\" +base64.b64encode(gif.read())\n",
    "IPython.display.Image(url=url.decode('ascii'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## Animated quiver\n",
    "Not only scatter plots can be animated, quiver as well, so the direction vector (vx, vy, vz) can also be animated, as shown in the example below, which is a (subsample of) a simulation of a small galaxy orbiting a host galaxy (not visible)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "shape of steam data (6, 200, 1250)\n"
     ]
    }
   ],
   "source": [
    "import ipyvolume.datasets\n",
    "stream = ipyvolume.datasets.animated_stream.fetch()\n",
    "print(\"shape of steam data\", stream.data.shape) # first dimension contains x, y, z, vx, vy, vz, then time, then particle"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "840e188e83a84be3b85be0699c02a863",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "A Jupyter Widget"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = p3.figure()\n",
    "# instead of doing x=stream.data[0], y=stream.data[1], ... vz=stream.data[5], use *stream.data\n",
    "# limit to 50 timesteps to avoid having a huge notebook\n",
    "q = p3.quiver(*stream.data[:,0:50,:200], color=\"red\", size=7)\n",
    "p3.style.use(\"dark\") # looks better\n",
    "p3.animation_control(q, interval=200)\n",
    "p3.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "fig.animation = 0 # set to 0 for no interpolation"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python [default]",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
