{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Jupyter Notebook backend demo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This example shows how vispy's low-level gloo interface can be used to display a Canvas in a notebook. By default, vispy will detect that it is being run in a notebook and load the \"jupyter_rfb\" backend automatically. This does require that the \"jupyter_rfb\" package be installed.\n",
    "\n",
    "Due to the way widgets work in Jupyter, you can display the Canvas multiple times, but they will continue to represent the same object. In the case of timers, they will continue to run in the backgroudn until they are stopped, even if the output of your notebook cell is cleared and no canvas is visible."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import vispy\n",
    "import vispy.gloo as gloo\n",
    "from vispy import app\n",
    "from vispy.util.transforms import perspective, translate, rotate"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n = 100\n",
    "a_position = np.random.uniform(-1, 1, (n, 3)).astype(np.float32)\n",
    "a_id = np.random.randint(0, 30, (n, 1))\n",
    "a_id = np.sort(a_id, axis=0).astype(np.float32)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "VERT_SHADER = \"\"\"\n",
    "uniform mat4 u_model;\n",
    "uniform mat4 u_view;\n",
    "uniform mat4 u_projection;\n",
    "attribute vec3 a_position;\n",
    "attribute float a_id;\n",
    "varying float v_id;\n",
    "void main (void) {\n",
    "    v_id = a_id;\n",
    "    gl_Position = u_projection * u_view * u_model * vec4(a_position,1.0);\n",
    "}\n",
    "\"\"\"\n",
    "\n",
    "FRAG_SHADER = \"\"\"\n",
    "varying float v_id;\n",
    "void main()\n",
    "{\n",
    "    float f = fract(v_id);\n",
    "    // The second useless test is needed on OSX 10.8 (fuck)\n",
    "    if( (f > 0.0001) && (f < .9999) )\n",
    "        discard;\n",
    "    else\n",
    "        gl_FragColor = vec4(0,0,0,1);\n",
    "}\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Canvas(app.Canvas):\n",
    "\n",
    "    # ---------------------------------\n",
    "    def __init__(self, size=None, show=True):\n",
    "        app.Canvas.__init__(self, keys='interactive', size=size)\n",
    "\n",
    "        self.program = gloo.Program(VERT_SHADER, FRAG_SHADER)\n",
    "\n",
    "        # Set uniform and attribute\n",
    "        self.program['a_id'] = gloo.VertexBuffer(a_id)\n",
    "        self.program['a_position'] = gloo.VertexBuffer(a_position)\n",
    "\n",
    "        self.translate = 5\n",
    "        self.view = translate((0, 0, -self.translate), dtype=np.float32)\n",
    "        self.model = np.eye(4, dtype=np.float32)\n",
    "\n",
    "        gloo.set_viewport(0, 0, self.physical_size[0], self.physical_size[1])\n",
    "        self.projection = perspective(45.0, self.size[0] /\n",
    "                                      float(self.size[1]), 1.0, 1000.0)\n",
    "        self.program['u_projection'] = self.projection\n",
    "\n",
    "        self.program['u_model'] = self.model\n",
    "        self.program['u_view'] = self.view\n",
    "\n",
    "        self.theta = 0\n",
    "        self.phi = 0\n",
    "\n",
    "        self.context.set_clear_color('white')\n",
    "        self.context.set_state('translucent')\n",
    "\n",
    "        self.timer = app.Timer('auto', connect=self.on_timer, start=True)\n",
    "\n",
    "        if show:\n",
    "            self.show()\n",
    "\n",
    "    # ---------------------------------\n",
    "    def on_key_press(self, event):\n",
    "        if event.text == ' ':\n",
    "            if self.timer.running:\n",
    "                self.timer.stop()\n",
    "            else:\n",
    "                self.timer.start()\n",
    "\n",
    "    # ---------------------------------\n",
    "    def on_timer(self, event):\n",
    "        self.theta += .5\n",
    "        self.phi += .5\n",
    "        self.model = np.dot(rotate(self.theta, (0, 0, 1)),\n",
    "                            rotate(self.phi, (0, 1, 0)))\n",
    "        self.program['u_model'] = self.model\n",
    "        self.update()\n",
    "\n",
    "    # ---------------------------------\n",
    "    def on_resize(self, event):\n",
    "        gloo.set_viewport(0, 0, event.physical_size[0], event.physical_size[1])\n",
    "        self.projection = perspective(45.0, event.size[0] /\n",
    "                                      float(event.size[1]), 1.0, 1000.0)\n",
    "        self.program['u_projection'] = self.projection\n",
    "\n",
    "    # ---------------------------------\n",
    "    def on_mouse_wheel(self, event):\n",
    "        self.translate += event.delta[1]\n",
    "        self.translate = max(2, self.translate)\n",
    "        self.view = translate((0, 0, -self.translate))\n",
    "        self.program['u_view'] = self.view\n",
    "        self.update()\n",
    "\n",
    "    # ---------------------------------\n",
    "    def on_draw(self, event):\n",
    "        self.context.clear()\n",
    "        self.program.draw('line_strip')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Every cell above was preparing our GL Canvas for operation. Now we will create the Canvas instance and because of the `self.show()` in our `__init__` method our canvas will be shown and its timer started immediately."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "c = Canvas(size=(300, 300))\n",
    "c"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When timers are involved we can run the `stop` and `start` methods to turn them on/off and see the result in the widget displayed above."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "c.timer.stop()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "c.timer.start()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.7.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
