{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Creating your own electrode array\n\nThis example shows how to create a new\n:py:class:`~pulse2percept.implants.ElectrodeArray` object.\n\nAs the base class for all electrode arrays in pulse2percept, the\n:py:class:`~pulse2percept.implants.ElectrodeArray` class provides a blue print\nfor the functionality that every electrode array should have.\n\nFirst and foremost, an :py:class:`~pulse2percept.implants.ElectrodeArray`\ncontains a collection of :py:class:`~pulse2percept.implants.Electrode` objects,\nand new electrodes can be added via the\n:py:func:`~pulse2percept.implants.ElectrodeArray.add_electrodes` method.\n\nIn addition, individual electrodes in the array can be accessed by indexing\nusing either their pre-assigned names (a string) or their place in the array\n(integer).\n\n## Arranging electrodes in a circle\n\nIn this example, we want to build a new type of electrode array that arranges\nall of its electrodes in a circle.\n\nTo do this, we need to create a new class ``CircleElectrodeArray`` that is\na child of :py:class:`~pulse2percept.implants.ElectrodeArray`:\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "```python\nclass CircleElectrodeArray(ElectrodeArray):\n    \"\"\"Electrodes arranged in a circle\"\"\"\n    ...\n```\nThis way, the ``CircleElectrodeArray`` class can access all public methods\nof :py:class:`~pulse2percept.implants.ElectrodeArray`.\n\nThe constructor then has the job of creating all electrodes in the array\nand placing them at the appropriate location; for example, by using the\n:py:func:`~pulse2percept.implants.ElectrodeArray.add_electrodes` method.\n\nThe constructor of the class should accept a number of arguments:\n\n- ``n_electrodes``: how many electrodes to arrange in a circle\n- ``radius``: the radius of the circle\n- ``x_center``: the x-coordinate of the center of the circle\n- ``y_center``: the y-coordinate of the center of the circle\n\nFor simplicity, we will use :py:class:`~pulse2percept.implants.DiskElectrode`\nobjects of a given radius (100um), although it would be relatively straightforward\nto allow the user to choose the electrode type.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from pulse2percept.implants import ElectrodeArray, DiskElectrode\nimport collections as coll\nimport numpy as np\n\n\nclass CircleElectrodeArray(ElectrodeArray):\n\n    def __init__(self, n_electrodes, radius, x_center, y_center):\n        \"\"\"Electrodes arranged in a circle\n\n        Electrodes will be named 'A0', 'A1', ...\n\n        Parameters\n        ----------\n        n_electrodes : int\n            how many electrodes to arrange in a circle\n        radius : float\n            the radius of the circle (microns)\n        x_center, y_center : float\n            the x,y coordinates of the center of the circle (microns),\n            where (0,0) is the center of the fovea\n        \"\"\"\n        # The job of the constructor is to create the electrodes. We start\n        # with an empty collection:\n        self._electrodes = coll.OrderedDict()\n        # We then generate a number `n_electrodes` of electrodes, arranged on\n        # the circumference of a circle:\n        for n in range(n_electrodes):\n            # Angular position of the electrode:\n            ang = 2.0 * np.pi / n_electrodes * n\n            # Create the disk electrode:\n            electrode = DiskElectrode(x_center + np.cos(ang) * radius,\n                                      y_center + np.sin(ang) * radius, 0, 100)\n            # Add the electrode to the collection:\n            self.add_electrode('A' + str(n), electrode)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Using the CircleElectrodeArray class\n\nTo use the new class, we need to specify all input arguments and pass them\nto the constructor:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "n_electrodes = 10\nradius = 1000  # radius in microns\nx_center = 0  # x-coordinate of circle center (microns)\ny_center = 0  # y-coordinate of circle center (microns)\n\n# Create a new instance of type CircleElectrodeArray:\nearray = CircleElectrodeArray(n_electrodes, radius, x_center, y_center)\nprint(earray)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Individual electrodes can be accessed by their name or integer index:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "earray[0]\n\nearray['A0']\n\nearray[0] == earray['A0']"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Visualizing the electrode array\n\nElectrode arrays come with their own plotting method:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "earray.plot()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "By default, the method will use the current Axes object or create a new one\nif none exists. Alternatively, you can specify ``ax=`` yourself.\n\n## Extending the CircleElectrodeArray class\n\nSimilar to extending :py:class:`~pulse2percept.implants.ElectrodeArray` for\nour purposes, we can extend ``CircleElectrodeArray``.\n\nTo add new functionality, we could simply edit the above constructor.\nHowever, nobody stops us from creating our own hierarchy of classes.\n\nFor example, we could build a ``FlexibleCircleElectrodeArray`` that allows us\nto remove individual electrodes from the array:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "class FlexibleCircleElectrodeArray(CircleElectrodeArray):\n\n    def remove(self, name):\n        \"\"\"Deletean electrode from the array\n\n        Parameters\n        ----------\n        name : int, string\n            the name of the electrode to be removed\n        \"\"\"\n        del self.electrodes[name]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Note how we didn't even specify a constructor.\nBy default, the class inherits all (public) functionality from its parent,\nincluding its constructor. So the following line will create the same\nelectrode array as above:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "flex_earray = FlexibleCircleElectrodeArray(\n    n_electrodes, radius, x_center, y_center)\nprint(flex_earray)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "A single electrode can be removed by passing its name to the ``remove``\nmethod:\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# Remove electrode 'A1'\nflex_earray.remove('A1')\n\n# Replot the implant:\nflex_earray.plot()"
      ]
    }
  ],
  "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.9"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}