diff --git a/examples/event_handler.ipynb b/examples/event_handler.ipynb new file mode 100644 index 000000000..69c835e2e --- /dev/null +++ b/examples/event_handler.ipynb @@ -0,0 +1,430 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a0b458c1-53c6-43d9-a72a-41f51bfe493d", + "metadata": {}, + "source": [ + "### notebook for learning event handler system" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "638b6a65-6d78-459c-ac88-35312233d22a", + "metadata": {}, +<<<<<<< HEAD + "outputs": [], +======= + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2022-12-19 11:35:14.246180: I tensorflow/core/platform/cpu_feature_guard.cc:193] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations: AVX2 AVX512F AVX512_VNNI FMA\n", + "To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.\n", + "2022-12-19 11:35:14.498269: I tensorflow/core/util/port.cc:104] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.\n", + "2022-12-19 11:35:14.542615: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /home/caitlin/venvs/mescore/lib/python3.9/site-packages/cv2/../../lib64:\n", + "2022-12-19 11:35:14.542629: I tensorflow/compiler/xla/stream_executor/cuda/cudart_stub.cc:29] Ignore above cudart dlerror if you do not have a GPU set up on your machine.\n", + "2022-12-19 11:35:15.350591: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /home/caitlin/venvs/mescore/lib/python3.9/site-packages/cv2/../../lib64:\n", + "2022-12-19 11:35:15.350720: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /home/caitlin/venvs/mescore/lib/python3.9/site-packages/cv2/../../lib64:\n", + "2022-12-19 11:35:15.350726: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.\n" + ] + }, + { + "ename": "ImportError", + "evalue": "cannot import name 'Graphic' from partially initialized module 'fastplotlib.graphics._base' (most likely due to a circular import) (/home/caitlin/repos/fastplotlib/fastplotlib/graphics/_base.py)", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mImportError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[1], line 5\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mmatplotlib\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m pyplot \u001b[38;5;28;01mas\u001b[39;00m plt\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mpandas\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mpd\u001b[39;00m\n\u001b[0;32m----> 5\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mfastplotlib\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m GridPlot, Image, Plot, Line, Heatmap\n\u001b[1;32m 6\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mscipy\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mspatial\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m distance\n\u001b[1;32m 7\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mipywidgets\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mwidgets\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m IntSlider, VBox\n", + "File \u001b[0;32m~/repos/fastplotlib/fastplotlib/__init__.py:1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mplot\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m Plot\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mpathlib\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m Path\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mwgpu\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mgui\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mauto\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m run\n", + "File \u001b[0;32m~/repos/fastplotlib/fastplotlib/plot.py:3\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mpygfx\u001b[39;00m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mwgpu\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mgui\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mauto\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m WgpuCanvas\n\u001b[0;32m----> 3\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mlayouts\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01m_subplot\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m Subplot\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m graphics\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mfunctools\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m partial\n", + "File \u001b[0;32m~/repos/fastplotlib/fastplotlib/layouts/__init__.py:1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01m_gridplot\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m GridPlot\n\u001b[1;32m 3\u001b[0m __all__ \u001b[38;5;241m=\u001b[39m [\n\u001b[1;32m 4\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mGridPlot\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5\u001b[0m ]\n", + "File \u001b[0;32m~/repos/fastplotlib/fastplotlib/layouts/_gridplot.py:5\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mpygfx\u001b[39;00m\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01m_defaults\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m create_controller\n\u001b[0;32m----> 5\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01m_subplot\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m Subplot\n\u001b[1;32m 6\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mtyping\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;241m*\u001b[39m\n\u001b[1;32m 7\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mwgpu\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mgui\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mauto\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m WgpuCanvas\n", + "File \u001b[0;32m~/repos/fastplotlib/fastplotlib/layouts/_subplot.py:3\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mpygfx\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m Scene, OrthographicCamera, PanZoomController, OrbitOrthoController, \\\n\u001b[1;32m 2\u001b[0m AxesHelper, GridHelper, WgpuRenderer, Background, BackgroundMaterial\n\u001b[0;32m----> 3\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mgraphics\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m HeatmapGraphic\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01m_defaults\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m create_camera, create_controller\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mtyping\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;241m*\u001b[39m\n", + "File \u001b[0;32m~/repos/fastplotlib/fastplotlib/graphics/__init__.py:1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mhistogram\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m HistogramGraphic\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mline\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m LineGraphic\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mscatter\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m ScatterGraphic\n", + "File \u001b[0;32m~/repos/fastplotlib/fastplotlib/graphics/histogram.py:7\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mnumpy\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mnp\u001b[39;00m\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mpygfx\u001b[39;00m\n\u001b[0;32m----> 7\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01m_base\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m Graphic\n\u001b[1;32m 10\u001b[0m \u001b[38;5;28;01mclass\u001b[39;00m \u001b[38;5;21;01m_HistogramBin\u001b[39;00m(pygfx\u001b[38;5;241m.\u001b[39mMesh):\n\u001b[1;32m 11\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__int__\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n", + "File \u001b[0;32m~/repos/fastplotlib/fastplotlib/graphics/_base.py:10\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mabc\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m ABC, abstractmethod\n\u001b[1;32m 9\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mdataclasses\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m dataclass\n\u001b[0;32m---> 10\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mlinecollection\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m LineCollection\n\u001b[1;32m 12\u001b[0m \u001b[38;5;28;01mclass\u001b[39;00m \u001b[38;5;21;01mGraphic\u001b[39;00m:\n\u001b[1;32m 13\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__init__\u001b[39m(\n\u001b[1;32m 14\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 15\u001b[0m data,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 20\u001b[0m name: \u001b[38;5;28mstr\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 21\u001b[0m ):\n", + "File \u001b[0;32m~/repos/fastplotlib/fastplotlib/graphics/linecollection.py:5\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mpygfx\u001b[39;00m\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mtyping\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m Union, List\n\u001b[0;32m----> 5\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mfastplotlib\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mgraphics\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01m_base\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m Graphic\n\u001b[1;32m 7\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mline\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m LineGraphic\n\u001b[1;32m 8\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mtyping\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;241m*\u001b[39m\n", + "\u001b[0;31mImportError\u001b[0m: cannot import name 'Graphic' from partially initialized module 'fastplotlib.graphics._base' (most likely due to a circular import) (/home/caitlin/repos/fastplotlib/fastplotlib/graphics/_base.py)" + ] + } + ], +>>>>>>> 24fb5a5 (blah) + "source": [ + "from mesmerize_core import *\n", + "import numpy as np\n", + "from matplotlib import pyplot as plt\n", + "import pandas as pd\n", + "from fastplotlib import GridPlot, Image, Plot, Line, Heatmap\n", + "from scipy.spatial import distance\n", + "from ipywidgets.widgets import IntSlider, VBox\n", + "import pygfx" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "80938adc-1775-4751-94dd-89fb94ed673a", + "metadata": {}, + "outputs": [], + "source": [ + "class Contour_Selection():\n", + " def __init__(\n", + " self,\n", + " gp: GridPlot,\n", + " coms,\n", + " ):\n", + " self.gp = gp\n", + " self.heatmap = self.gp.subplots[0, 1].scene.children[0]\n", + " self.image = None\n", + " self._contour_index = None \n", + " \n", + " for child in self.gp.subplots[0, 0].scene.children:\n", + " if isinstance(child, pygfx.Image):\n", + " self.image = child\n", + " break;\n", + " if self.image == None:\n", + " raise ValueError(\"No image found!\")\n", + " self.coms = np.array(coms)\n", + " \n", + " self.image.add_event_handler(self.event_handler, \"click\")\n", + " \n", + " # first need to add event handler for when contour is clicked on\n", + " # should also trigger highlighting in heatmap\n", + " def event_handler(self, event):\n", + " if self._contour_index is not None:\n", + " self.remove_highlight()\n", + " self.add_highlight(event)\n", + " else:\n", + " self.add_highlight(event)\n", + " \n", + " def add_highlight(self, event):\n", + " click_location = np.array(event.pick_info[\"index\"])\n", + " self._contour_index = np.linalg.norm((self.coms - click_location), axis=1).argsort()[0] + 1\n", + " line = self.gp.subplots[0, 0].scene.children[self._contour_index]\n", + " line.geometry.colors.data[:] = np.array([1.0, 1.0, 1.0, 1.0]) \n", + " line.geometry.colors.update_range()\n", + " #self.heatmap.add_highlight(self._contour_index)\n", + " \n", + " def remove_highlight(self):\n", + " # change color of highlighted index back to normal\n", + " line = self.gp.subplots[0, 0].scene.children[self._contour_index]\n", + " line.geometry.colors.data[:] = np.array([1., 0., 0., 0.7]) \n", + " line.geometry.colors.update_range()\n", + " # for h in self.heatmap._highlights:\n", + " # self.heatmap.remove_highlight(h)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "7b3129e6-5d82-4f39-b887-e584421c3a74", + "metadata": {}, + "outputs": [], + "source": [ + "set_parent_raw_data_path(\"/home/kushal/caiman_data/\")\n", + "\n", + "batch_path = \"/home/clewis7/caiman_data/cnmf_practice/batch.pickle\"\n", + "\n", + "movie_path = \"/home/kushal/caiman_data/example_movies/Sue_2x_3000_40_-46.tif\"" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "776c6dd8-cf07-47ee-bcc4-5fe84bc030f5", + "metadata": {}, + "outputs": [], + "source": [ + "df = load_batch(batch_path)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "b22fd79b-5a7a-48fb-898c-4d3f3a34c118", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
algoitem_nameinput_movie_pathparamsoutputscommentsuuid
0mcorrmy_movieexample_movies/Sue_2x_3000_40_-46.tif{'main': {'max_shifts': (24, 24), 'strides': (...{'mean-projection-path': 1ed8feb3-9fc8-4a78-8f...None1ed8feb3-9fc8-4a78-8f6d-164620822016
1cnmfmy_movie1ed8feb3-9fc8-4a78-8f6d-164620822016/1ed8feb3-...{'main': {'fr': 30, 'p': 1, 'nb': 2, 'merge_th...{'mean-projection-path': 5f4e3e27-ac1f-4ede-90...None5f4e3e27-ac1f-4ede-903b-be43bd81fddc
\n", + "
" + ], + "text/plain": [ + " algo item_name input_movie_path \\\n", + "0 mcorr my_movie example_movies/Sue_2x_3000_40_-46.tif \n", + "1 cnmf my_movie 1ed8feb3-9fc8-4a78-8f6d-164620822016/1ed8feb3-... \n", + "\n", + " params \\\n", + "0 {'main': {'max_shifts': (24, 24), 'strides': (... \n", + "1 {'main': {'fr': 30, 'p': 1, 'nb': 2, 'merge_th... \n", + "\n", + " outputs comments \\\n", + "0 {'mean-projection-path': 1ed8feb3-9fc8-4a78-8f... None \n", + "1 {'mean-projection-path': 5f4e3e27-ac1f-4ede-90... None \n", + "\n", + " uuid \n", + "0 1ed8feb3-9fc8-4a78-8f6d-164620822016 \n", + "1 5f4e3e27-ac1f-4ede-903b-be43bd81fddc " + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "03045957-837e-49e3-83aa-3e069af977a1", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3b283ef0f3084d828f80fd3a6cb25413", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "RFBOutputContext()" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8b104ac5f10e4d53866953a4401593df", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(JupyterWgpuCanvas(), IntSlider(value=0, max=2999)))" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "gp = GridPlot(shape=(1,2))\n", + "\n", + "contours, coms = df.iloc[-1].cnmf.get_contours()\n", + "movie = df.iloc[-1].cnmf.get_input_memmap()\n", + "temporal = df.iloc[-1].cnmf.get_temporal()\n", + "\n", + "contour_graphic = Image(movie[0].T, cmap=\"gnuplot2\")\n", + "heatmap = Heatmap(data=temporal[:,0:1000], cmap=\"jet\")\n", + "\n", + "slider = IntSlider(value=0, min=0, max=movie.shape[0] - 1, step=1)\n", + "\n", + "gp.subplots[0,0].add_graphic(contour_graphic)\n", + "gp.subplots[0,1].add_graphic(heatmap)\n", + "\n", + "for coor in contours:\n", + " # line data has to be 3D\n", + " zs = np.ones(coor.shape[0]) # this will place it above the image graphic\n", + " c3d = [coor[:, 0], coor[:, 1], zs]\n", + " coors_3d = np.dstack(c3d)[0]\n", + "\n", + " # make all the lines red, [R, G, B, A] array\n", + " colors = np.vstack([[1., 0., 0., 0.7]] * coors_3d.shape[0])\n", + " line_graphic = Line(data=coors_3d, colors=colors, zlevel=1)\n", + " gp.subplots[0, 0].add_graphic(line_graphic)\n", + "\n", + "previous_slider_value = 0\n", + "def update_frame(): \n", + " if slider.value == previous_slider_value:\n", + " return\n", + " contour_graphic.update_data(data=movie[slider.value].T)\n", + "\n", + "gp.add_animations([update_frame])\n", + "\n", + "VBox([gp.show(), slider])" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "2bf4a1ea-3559-4846-bc32-4dddaca2d470", + "metadata": {}, + "outputs": [], + "source": [ + "contour_selection = Contour_Selection(gp=gp, coms=coms)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "5ecdbdea-4e77-462f-a18d-5ed9b45faada", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(155, 3000)" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temporal.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "51b3feea-af91-4158-97a2-8dbadd2480b5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "numpy.ndarray" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(coms[0])" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "3b2011f4-046b-4396-a592-81a5128d2482", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([7.12861818, 9.84114483])" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "coms[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "c5791bf4-a116-4093-bce1-eee7bc25221c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gp.subplots[0, 1]" + ] + }, + { + "cell_type": "markdown", + "id": "ae9cce16-370f-44d2-b532-dc442cce379d", + "metadata": {}, + "source": [ + "next steps:\n", + " clicking on a contour should highlight it and the heatmap row" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/linecollection_event.ipynb b/examples/linecollection_event.ipynb new file mode 100644 index 000000000..d5aaabacf --- /dev/null +++ b/examples/linecollection_event.ipynb @@ -0,0 +1,302 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "id": "0c32716f-320e-4021-ad60-1c142fe6fd56", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "6d72f23c-0f3a-4b2c-806d-3b239237c725", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from fastplotlib.graphics import ImageGraphic, LineCollection\n", + "from fastplotlib import GridPlot\n", + "import pickle" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "a8514bb3-eef5-4fd1-bcbf-9c50173a9a3c", + "metadata": {}, + "outputs": [], + "source": [ + "def auto_scale(p):\n", + " p.camera.maintain_aspect = False\n", + " width, height, depth = np.ptp(p.scene.get_world_bounding_box(), axis=0)\n", + " p.camera.width = width\n", + " p.camera.height = height\n", + "\n", + " p.controller.distance = 0\n", + " \n", + " p.controller.zoom(0.8 / p.controller.zoom_value)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "2fb13990-63fc-4fc6-b5c1-93f8ec4c1572", + "metadata": {}, + "outputs": [], + "source": [ + "contours = pickle.load(open(\"/home/kushal/caiman_data/contours.pickle\", \"rb\"))[0]\n", + "temporal = pickle.load(open(\"/home/kushal/caiman_data/temporal.pickle\", \"rb\"))\n", + "temporal += temporal.min()\n", + "\n", + "# make it a stack of traces\n", + "y_zero = 0\n", + "sep = 10\n", + "for i in range(1, temporal.shape[0]):\n", + " y_zero = temporal[i - 1].max()\n", + " temporal[i] += y_zero + sep\n", + "\n", + "# random colors\n", + "colors = np.random.rand(len(contours), 4).astype(np.float32)\n", + "colors[:, -1] = 1" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "654da73f-d20c-4a0f-bd99-13c1a52f5f5a", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "98ad6155b7c34241bd705d0f40bce8c0", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "RFBOutputContext()" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
initial snapshot
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8906e4b8e78c465ca05f088f105de2fc", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "JupyterWgpuCanvas()" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# img and contour plot\n", + "plot = GridPlot(shape=(1, 2))\n", + "\n", + "data = np.ones(shape=(175, 175))\n", + "\n", + "line_collection = LineCollection(data=contours, z_position=[[1]] * len(contours), colors=colors.tolist())\n", + "plot[0, 0].add_graphic(line_collection)\n", + "\n", + "img = ImageGraphic(data=data)\n", + "plot[0, 0].add_graphic(img)\n", + "\n", + "\n", + "temporal_coll = LineCollection(data=temporal, colors=colors.tolist())\n", + "plot[0, 1].add_graphic(temporal_coll)\n", + "\n", + "plot.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "0836f4fc-fb3b-44c6-8515-ab8d63dff52b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "line_collection._world_object.parent" + ] + }, + { + "cell_type": "markdown", + "id": "ae5fe95b-88be-48c7-a4a6-51d2818fbff0", + "metadata": {}, + "source": [ + "# you need to run this to make the stacked lineplot visible, it's easier in the latest master with camera auto-scaling" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "8597de09-94aa-44cd-b480-acc1758a198c", + "metadata": {}, + "outputs": [], + "source": [ + "plot[0, 1].controller.distance = 0\n", + "auto_scale(plot[0, 1])" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "e321cf0d-52f7-4da2-983a-ff10653093bb", + "metadata": {}, + "outputs": [], + "source": [ + "white = list()\n", + "for contour in line_collection:\n", + " white.append(np.ones(shape=contour.colors.shape))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "01296977-1664-40ae-86fb-ed515fa96f4a", + "metadata": {}, + "outputs": [], + "source": [ + "white_temporal = np.ones((len(contours), 4)).astype(np.float32)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "4430e805-54db-4218-967a-30290ced8ca9", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import *" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "c02210db-5347-4e97-a551-f4f362d3910a", + "metadata": {}, + "outputs": [], + "source": [ + "def indices_mapper(target: Any, indices: np.array) -> int:\n", + " # calculate coms of line collection \n", + " \n", + " coms = list()\n", + "\n", + " for contour in target.data:\n", + " coors = contour.data[~np.isnan(contour.data).any(axis=1)]\n", + " com = coors.mean(axis=0)\n", + " coms.append(com)\n", + "\n", + " # euclidean distance to find closest index of com \n", + " indices = np.append(indices, [0])\n", + " \n", + " ix = np.linalg.norm((coms - indices), axis=1).argsort()[0] \n", + " \n", + " #return that index to set feature \n", + " return ix" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "0fa4033f-323b-421c-84ad-da34a0ac177c", + "metadata": {}, + "outputs": [], + "source": [ + "# until we create an event \"color-changed\" (and for other graphic features)\n", + "# later we can just use the \"color-changed\" event from contour to change the lineplot or heatmap etc.\n", + "def indices_mapper_temporal(target, indices):\n", + " # global since we don't have something like \"color changed\"\n", + " # as an event which we can used for stakced line plots\n", + " global contours\n", + " coms = list()\n", + "\n", + " for contour in contours:\n", + " coors = contour[~np.isnan(contour.data).any(axis=1)]\n", + " com = coors.mean(axis=0)\n", + " coms.append(com)\n", + " \n", + " ix = np.linalg.norm((np.array(coms) - np.array(indices)), axis=1).argsort()[0]\n", + " print(ix)\n", + " \n", + " #return that index to set feature \n", + " return ix" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "0d3a3665-c9e5-42e4-9d61-c02f1f401ee2", + "metadata": {}, + "outputs": [], + "source": [ + "img.link(event_type=\"click\", target=line_collection, feature=\"colors\", new_data=white, indices_mapper=indices_mapper)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "14fdbae1-31b7-4b58-a7e7-a50589f0ff0d", + "metadata": {}, + "outputs": [], + "source": [ + "img.link(event_type=\"click\", target=temporal_coll, feature=\"colors\", new_data=white_temporal, indices_mapper=indices_mapper_temporal)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/lineplot.ipynb b/examples/lineplot.ipynb index 7561efe88..d00346daf 100644 --- a/examples/lineplot.ipynb +++ b/examples/lineplot.ipynb @@ -178,7 +178,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.5" + "version": "3.9.2" } }, "nbformat": 4, diff --git a/examples/single_contour_event.ipynb b/examples/single_contour_event.ipynb new file mode 100644 index 000000000..3c88b72c8 --- /dev/null +++ b/examples/single_contour_event.ipynb @@ -0,0 +1,251 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "3a992f41-b157-4b6f-9630-ef370389f318", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "589eaea4-e749-46ff-ac3d-e22aa4f75641", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from fastplotlib.graphics import LineGraphic\n", + "from fastplotlib.plot import Plot\n", + "import pickle" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "650279ac-e7df-4c6f-aac1-078ae4287028", + "metadata": {}, + "outputs": [], + "source": [ + "contours = pickle.load(open(\"/home/caitlin/Downloads/contours.pickle\", \"rb\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "49dc6123-39d8-4f60-b14b-9cfd9a008940", + "metadata": {}, + "outputs": [], + "source": [ + "single_contour = LineGraphic(data=contours[0], size=10.0, cmap=\"jet\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "776e916f-16c9-4114-b1ff-7ea209aa7b04", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ef7e51b0da07486faf42b012582be35e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "RFBOutputContext()" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "MESA-INTEL: warning: Performance support disabled, consider sysctl dev.i915.perf_stream_paranoid=0\n", + "\n" + ] + } + ], + "source": [ + "plot = Plot()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "203768f0-6bb4-4ba9-b099-395f2bdd2a8c", + "metadata": {}, + "outputs": [], + "source": [ + "plot.add_graphic(single_contour)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "4e46d687-d81a-4b6f-bece-c9edf3606d4f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
initial snapshot
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9b0cb33f7b674585b2f5f58c7d1af28f", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "JupyterWgpuCanvas()" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "plot.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "dbcda1d6-3f21-4a5e-b60f-75bf9103fbe6", + "metadata": {}, + "outputs": [], + "source": [ + "white = np.ones(shape=single_contour.colors.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "6db453cf-5856-4a7b-879f-83032cb9e9ac", + "metadata": {}, + "outputs": [], + "source": [ + "single_contour.link(event_type=\"click\", target=single_contour, feature=\"colors\", new_data=white, indices_mapper=None)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "712c3f43-3339-4d1b-9d64-fe4f4d6bd672", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'click': [CallbackData(target=fastplotlib.LineGraphic @ 0x7ff9ce507d30, feature='colors', new_data=array([[1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.]]), old_data=array([[0. , 0. , 0.5 , 1. ],\n", + " [0. , 0. , 0.6604278 , 1. ],\n", + " [0. , 0. , 0.8386809 , 1. ],\n", + " [0. , 0. , 1. , 1. ],\n", + " [0. , 0.11176471, 1. , 1. ],\n", + " [0. , 0.26862746, 1. , 1. ],\n", + " [0. , 0.40980393, 1. , 1. ],\n", + " [0. , 0.56666666, 1. , 1. ],\n", + " [0. , 0.7235294 , 1. , 1. ],\n", + " [0. , 0.88039213, 0.9835547 , 1. ],\n", + " [0.11068944, 1. , 0.8570525 , 1. ],\n", + " [0.22454143, 1. , 0.7432005 , 1. ],\n", + " [0.35104364, 1. , 0.61669827, 1. ],\n", + " [0.47754586, 1. , 0.49019608, 1. ],\n", + " [0.6040481 , 1. , 0.36369386, 1. ],\n", + " [0.7305503 , 1. , 0.23719165, 1. ],\n", + " [0.84440225, 1. , 0.12333966, 1. ],\n", + " [0.97090447, 0.95933187, 0. , 1. ],\n", + " [1. , 0.8140886 , 0. , 1. ],\n", + " [1. , 0.6688453 , 0. , 1. ],\n", + " [1. , 0.523602 , 0. , 1. ],\n", + " [1. , 0.3928831 , 0. , 1. ],\n", + " [1. , 0.24763979, 0. , 1. ],\n", + " [1. , 0.10239651, 0. , 1. ],\n", + " [0.8565062 , 0. , 0. , 1. ],\n", + " [0.6782531 , 0. , 0. , 1. ],\n", + " [0.5 , 0. , 0. , 1. ]], dtype=float32))]}" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "single_contour.registered_callbacks" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d353d7b-a0d0-4629-a8c0-87b767d99bd2", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/fastplotlib/graphics/__init__.py b/fastplotlib/graphics/__init__.py index cad6de8c7..6294637aa 100644 --- a/fastplotlib/graphics/__init__.py +++ b/fastplotlib/graphics/__init__.py @@ -12,6 +12,6 @@ "LineGraphic", "HistogramGraphic", "HeatmapGraphic", - "LineCollection", - "TextGraphic" + "TextGraphic", + "LineCollection" ] diff --git a/fastplotlib/graphics/_base.py b/fastplotlib/graphics/_base.py index a1a2633b9..b729eb2a5 100644 --- a/fastplotlib/graphics/_base.py +++ b/fastplotlib/graphics/_base.py @@ -1,10 +1,13 @@ from typing import * +import numpy as np import pygfx from ..utils import get_colors from .features import GraphicFeature, DataFeature, ColorFeature, PresentFeature +from abc import ABC, abstractmethod +from dataclasses import dataclass class Graphic: def __init__( @@ -46,6 +49,7 @@ def __init__( self.colors = None self.name = name + self.registered_callbacks = dict() if n_colors is None: n_colors = self.data.feature_data.shape[0] @@ -60,23 +64,20 @@ def __init__( # useful for bbox calculations to ignore these Graphics self.present = PresentFeature(parent=self) - valid_features = ["visible"] + #valid_features = ["visible"] + self._feature_events = list() for attr_name in self.__dict__.keys(): attr = getattr(self, attr_name) if isinstance(attr, GraphicFeature): - valid_features.append(attr_name) + self._feature_events.append(attr_name) - self._valid_features = tuple(valid_features) + self._feature_events = tuple(self._feature_events) + self._pygfx_events = ("click",) @property def world_object(self) -> pygfx.WorldObject: return self._world_object - @property - def interact_features(self) -> Tuple[str]: - """The features for this ``Graphic`` that support interaction.""" - return self._valid_features - @property def visible(self) -> bool: return self.world_object.visible @@ -104,3 +105,55 @@ def __repr__(self): return f"'{self.name}' fastplotlib.{self.__class__.__name__} @ {hex(id(self))}" else: return f"fastplotlib.{self.__class__.__name__} @ {hex(id(self))}" + +class Interaction(ABC): + @abstractmethod + def _set_feature(self, feature: str, new_data: Any, indices: Any): + pass + + @abstractmethod + def _reset_feature(self, feature: str): + pass + + def link(self, event_type: str, target: Any, feature: str, new_data: Any, indices_mapper: callable = None): + if event_type in self._pygfx_events: + self.world_object.add_event_handler(self.event_handler, event_type) + elif event_type in self._feature_events: + feature = getattr(self, event_type) + feature.add_event_handler(self.event_handler, event_type) + else: + raise ValueError("event not possible") + + if event_type in self.registered_callbacks.keys(): + self.registered_callbacks[event_type].append( + CallbackData(target=target, feature=feature, new_data=new_data, indices_mapper=indices_mapper)) + else: + self.registered_callbacks[event_type] = list() + self.registered_callbacks[event_type].append( + CallbackData(target=target, feature=feature, new_data=new_data, indices_mapper=indices_mapper)) + + def event_handler(self, event): + event_info = event.pick_info + #click_info = np.array(event.pick_info["index"]) + if event.type in self.registered_callbacks.keys(): + for target_info in self.registered_callbacks[event.type]: + if target_info.indices_mapper is not None: + indices = target_info.indices_mapper(source=self, target=target_info.target, indices=click_info) + else: + indices = None + # set feature of target at indice using new data + target_info.target._set_feature(feature=target_info.feature, new_data=target_info.new_data, indices=indices) + +@dataclass +class CallbackData: + """Class for keeping track of the info necessary for interactivity after event occurs.""" + target: Any + feature: str + new_data: Any + indices_mapper: callable = None + +@dataclass +class PreviouslyModifiedData: + """Class for keeping track of previously modified data at indices""" + previous_data: Any + previous_indices: Any diff --git a/fastplotlib/graphics/heatmap.py b/fastplotlib/graphics/heatmap.py index 2c33564db..103a0fc2e 100644 --- a/fastplotlib/graphics/heatmap.py +++ b/fastplotlib/graphics/heatmap.py @@ -53,24 +53,18 @@ def __init__( ): """ Create a Heatmap Graphic - Parameters ---------- data: array-like, must be 2-dimensional | array-like, usually numpy.ndarray, must support ``memoryview()`` | Tensorflow Tensors also work _I think_, but not thoroughly tested - vmin: int, optional minimum value for color scaling, calculated from data if not provided - vmax: int, optional maximum value for color scaling, calculated from data if not provided - cmap: str, optional colormap to use to display the image data, default is ``"plasma"`` - selection_options - args: additional arguments passed to Graphic kwargs: @@ -140,4 +134,4 @@ def add_highlight(self, event): self.world_object.add(self.selection_graphic) self._highlights.append(self.selection_graphic) - return rval + return rval \ No newline at end of file diff --git a/fastplotlib/graphics/image.py b/fastplotlib/graphics/image.py index 77c531c8a..ae7e47ca6 100644 --- a/fastplotlib/graphics/image.py +++ b/fastplotlib/graphics/image.py @@ -3,11 +3,11 @@ import numpy as np import pygfx -from ._base import Graphic +from ._base import Graphic, Interaction from ..utils import quick_min_max, get_cmap_texture -class ImageGraphic(Graphic): +class ImageGraphic(Graphic, Interaction): def __init__( self, data: Any, @@ -72,6 +72,12 @@ def __init__( pygfx.ImageBasicMaterial(clim=(vmin, vmax), map=get_cmap_texture(cmap)) ) + def _set_feature(self, feature: str, new_data: Any, indices: Any): + pass + + def _reset_feature(self, feature: str, old_data: Any): + pass + @property def clim(self) -> Tuple[float, float]: return self.world_object.material.clim diff --git a/fastplotlib/graphics/line.py b/fastplotlib/graphics/line.py index edf99e43c..32df62efc 100644 --- a/fastplotlib/graphics/line.py +++ b/fastplotlib/graphics/line.py @@ -2,10 +2,10 @@ import numpy as np import pygfx -from ._base import Graphic +from ._base import Graphic, Interaction, PreviouslyModifiedData -class LineGraphic(Graphic): +class LineGraphic(Graphic, Interaction): def __init__( self, data: Any, @@ -18,25 +18,19 @@ def __init__( ): """ Create a line Graphic, 2d or 3d - Parameters ---------- data: array-like Line data to plot, 2D must be of shape [n_points, 2], 3D must be of shape [n_points, 3] - z_position: float, optional z-axis position for placing the graphic - size: float, optional thickness of the line - colors: str, array, or iterable specify colors as a single human readable string, a single RGBA array, or an iterable of strings or RGBA arrays - cmap: str, optional apply a colormap to the line instead of assigning colors manually - args passed to Graphic kwargs @@ -59,3 +53,36 @@ def __init__( ) self.world_object.position.z = z_position + + def _set_feature(self, feature: str, new_data: Any, indices: Any = None): + if not hasattr(self, "_previous_data"): + self._previous_data = {} + elif hasattr(self, "_previous_data"): + self._reset_feature(feature) + if feature in self._feature_events: + feature_instance = getattr(self, feature) + if indices is not None: + previous = feature_instance[indices].copy() + feature_instance[indices] = new_data + else: + previous = feature_instance[:].copy() + feature_instance[:] = new_data + if feature in self._previous_data.keys(): + self._previous_data[feature].previous_data = previous + self._previous_data[feature].previous_indices = indices + else: + self._previous_data[feature] = PreviouslyModifiedData(previous_data=previous, previous_indices=indices) + else: + raise ValueError("name arg is not a valid feature") + + + def _reset_feature(self, feature: str): + if feature not in self._previous_data.keys(): + raise ValueError("no previous data registered for this feature") + else: + feature_instance = getattr(self, feature) + if self._previous_data[feature].previous_indices is not None: + feature_instance[self._previous_data[feature].previous_indices] = self._previous_data[feature].previous_data + else: + feature_instance[:] = self._previous_data[feature].previous_data + diff --git a/fastplotlib/graphics/linecollection.py b/fastplotlib/graphics/linecollection.py index ec4b1e4dd..7f0d24a33 100644 --- a/fastplotlib/graphics/linecollection.py +++ b/fastplotlib/graphics/linecollection.py @@ -1,13 +1,22 @@ import numpy as np import pygfx -from typing import Union -from .line import LineGraphic +from typing import Union, List + +from fastplotlib.graphics.line import LineGraphic from typing import * +from fastplotlib.graphics._base import Interaction +from abc import ABC, abstractmethod +class LineCollection: + def __init__(self, data: List[np.ndarray], + z_position: Union[List[float], float] = None, + size: Union[float, List[float]] = 2.0, + colors: Union[List[np.ndarray], np.ndarray] = None, + cmap: Union[List[str], str] = None, + *args, + **kwargs): -class LineCollection(): - def __init__(self, data: List[np.ndarray], z_position: Union[List[float], float] = None, size: Union[float, List[float]] = 2.0, colors: Union[List[np.ndarray], np.ndarray] = None, - cmap: Union[List[str], str] = None, *args, **kwargs): + self.name = None if not isinstance(z_position, float) and z_position is not None: if not len(data) == len(z_position): @@ -22,7 +31,8 @@ def __init__(self, data: List[np.ndarray], z_position: Union[List[float], float] if not len(data) == len(cmap): raise ValueError("args must be the same length") - self.collection = list() + self.data = list() + self._world_object = pygfx.Group() for i, d in enumerate(data): if isinstance(z_position, list): @@ -45,10 +55,33 @@ def __init__(self, data: List[np.ndarray], z_position: Union[List[float], float] else: _cmap = cmap - self.collection.append(LineGraphic(d, _z, _size, _colors, _cmap)) + lg = LineGraphic(d, _z, _size, _colors, _cmap) + self.data.append(lg) + self._world_object.add(lg.world_object) + + # TODO: make a base class for Collection graphics and put this as a base method + @property + def world_object(self) -> pygfx.WorldObject: + return self._world_object + + def _set_feature(self, feature: str, new_data: Any, indices: Any): + if feature in self.features: + update_func = getattr(self.data[indices], f"update_{feature}") + # if indices is a single indices or list of indices + self.data[indices].update_colors(new_data) + else: + raise ValueError("name arg is not a valid feature") + + def _reset_feature(self, feature: str, old_data: Any): + if feature in self.features: + #update_func = getattr(self, f"update_{feature}") + for i, line in enumerate(self.data): + line.update_colors(old_data[i]) + else: + raise ValueError("name arg is not a valid feature") def __getitem__(self, item): - return self.collection[item] + return self.data[item] pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy